diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/sfile/sf_file.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfile/sf_file.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,3644 @@ +// Copyright (c) 1995-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 "sf_std.h" +#include "sf_file_cache.h" +#include "cl_std.h" + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + +TInt OutputTraceInfo(CFsRequest* aRequest,TCorruptNameRec* aNameRec) + { + RThread tT; + RProcess tP; + TBool nameUnknown=EFalse; + TInt r=aRequest->Message().Client(tT,EOwnerThread); + if(r!=KErrNone) + { + nameUnknown=ETrue; + } + else + { + r=tT.Process(tP); + if(r!=KErrNone) + { + tT.Close(); + nameUnknown=ETrue; + } + } + TName n; + if(!nameUnknown) + { + n=tP.Name(); + TInt b=n.Locate('['); + if (b>=0) + n.SetLength(b); + tP.Close(); + tT.Close(); + } + else + { + n=_L("*Unknown*"); + } + TPtrC t(aRequest->Src().FullName()); + // output a message via the debug port + RDebug::Print(_L("@@@@ Corrupt file check %S tried to open %S"),&n,&t); + // make a new log record & chain it in + TCorruptLogRec* pLogRec= new TCorruptLogRec; + if(pLogRec==NULL) + return KErrNoMemory; + TPtrC nPtr(n); + if(pLogRec->Construct(aNameRec,&nPtr,gCorruptLogRecordList)!=KErrNone) + { + delete pLogRec; + return KErrNoMemory; + } + else + { + gCorruptLogRecordList=pLogRec; + // really a count of number of log records + gNumberOfCorruptHits++; + } + return KErrNone; + } + +TCorruptLogRec::TCorruptLogRec() + :iProcessName(NULL),iNameRec(NULL),iNext(NULL) + {} + +TCorruptLogRec::~TCorruptLogRec() + { // free off name memory + delete iProcessName; + } + + +void TCorruptLogRec::DestroyList() + { + TCorruptLogRec* pList=gCorruptLogRecordList; + + while(pList!=NULL) + { + TCorruptLogRec* pThis=pList; + pList=pList->iNext; + delete pThis; + } + gCorruptLogRecordList=NULL; + gNumberOfCorruptHits=0; + } + +TInt TCorruptLogRec::Construct(TCorruptNameRec* aNameRec, TPtrC* aProcessName, TCorruptLogRec* aChain) + { + iProcessName=aProcessName->Alloc(); + if(iProcessName==NULL) + return KErrNoMemory; + + iNameRec=aNameRec; + iNext=aChain; + return KErrNone; + } + +TInt TCorruptLogRec::GetLogRecord(TFsDebugCorruptLogRecordBuf& aLogRecord,TInt aLogRecNum) + { + if(aLogRecNum<=0) + { + return KErrArgument; + } + else if(aLogRecNum>gNumberOfCorruptHits) + { + return KErrNotFound; + } + + TCorruptLogRec* pList=gCorruptLogRecordList; + + for(TInt i=1;iiNext; + } + + TInt r=KErrNotFound; + + if(pList) + { + aLogRecord().iProcessName=pList->iProcessName->Des(); + aLogRecord().iFileName=pList->iNameRec->Name(); + aLogRecord().iError=pList->iNameRec->ReturnCode(); + r=KErrNone; + } + + return r; + } + +TCorruptNameRec::TCorruptNameRec() +:iName(NULL),iNext(NULL){} + +TInt TCorruptNameRec::Construct(TPtr* aName,TInt aReturnCode, TBool aUseOnce, TCorruptNameRec* aChain) + { + iName=aName->Alloc(); + if(iName==NULL) + return KErrNoMemory; + iReturnCode=aReturnCode; + iUseOnce=aUseOnce; + iConsumed=EFalse; + iNext=aChain; + return KErrNone; + } + +void TCorruptNameRec::ResetListConsumed() + { + TCorruptNameRec* pList=gCorruptFileNameList; + while(pList!=NULL) + { + pList->iConsumed=EFalse; + pList=pList->Next(); + } + } + +LOCAL_C void checkCorruptNamesList(CFsRequest* aRequest, TInt &aError) + { + aError=KErrNone; + TPtrC path(aRequest->Src().FullName()); + TCorruptNameRec* pList=gCorruptFileNameList; + while(pList) + { + if(pList->Name().MatchF(path)==0) + { + if(!pList->Consumed()) + { + aError=pList->ReturnCode(); + pList->SetConsumed(); + OutputTraceInfo(aRequest,pList); + } + break; + } + pList=pList->Next(); + } + } +#endif + + +LOCAL_C TInt DoInitNoParse(CFsRequest* aRequest) +// +// Common init for read and write access to files +// + { + CFileShare* share = GetShareFromHandle(aRequest->Session(), aRequest->Message().Int3()); + if(!share) + return(KErrBadHandle); + aRequest->SetDrive(&share->File().Drive()); + aRequest->SetScratchValue64( MAKE_TINT64(ETrue, (TUint) share) ); + return KErrNone; + } + +_LIT(KDrivePath,"?:"); +LOCAL_C TInt DoInitialise(CFsRequest* aRequest) +// +// Common initialisation code use file share to determine asychronicity +// + { + CFileShare* share = GetShareFromHandle(aRequest->Session(), aRequest->Message().Int3()); + if(!share) + return(KErrBadHandle); + aRequest->SetDrive(&share->File().Drive()); + aRequest->SetScratchValue64( MAKE_TINT64(ETrue, (TUint) share) ); + TBuf<2> drive(KDrivePath); + drive[0]=TText(aRequest->DriveNumber()+'A'); + aRequest->Src().Set(share->File().FileName(),NULL,&drive); + return KErrNone; + } + +LOCAL_C TInt InitialiseScratchToShare(CFsRequest* aRequest) +// +// Common code used to initialise the scratch value to the CFileShare* from the request +// + { + CFileShare* share=GetShareFromHandle(aRequest->Session(), aRequest->Message().Int3()); + if(!share) + return(KErrBadHandle); + aRequest->SetScratchValue64( MAKE_TINT64(ETrue, (TUint) share) ); + + return(KErrNone); + } + +LOCAL_C TInt FsFileOpenL(CFsRequest* aRequest, TFileOpen anOpen) +// +// Open a file. +// + { + TInt r; + + TUint32 mode=aRequest->Message().Int1(); + if (anOpen==EFileCreate || anOpen==EFileReplace) + { + r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + mode|=EFileWrite; + } + + TInt h; + r=aRequest->Drive()->FileOpen(aRequest,h,aRequest->Src().FullName().Mid(2),mode,anOpen); + if (r!=KErrNone) + return(r); + + TPtrC8 pH((TUint8*)&h,sizeof(TInt)); + TRAP(r, aRequest->WriteL(KMsgPtr3,pH)) + CheckForLeaveAfterOpenL(r, aRequest, h); + aRequest->Session()->IncResourceCount(); + + return(KErrNone); + } + +TInt TFsFileOpen::DoRequestL(CFsRequest* aRequest) +// +// +// + { + __PRINT(_L("TFsFileOpen::DoRequestL(CFsRequest* aRequest)")); + return FsFileOpenL(aRequest, EFileOpen); + } + +TInt TFsFileCreate::DoRequestL(CFsRequest* aRequest) +// +// +// + { + + __PRINT(_L("TFsFileCreate::DoRequestL(CFsRequest* aRequest)")); + return FsFileOpenL(aRequest, EFileCreate); + } + +TInt TFsFileCreate::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r=ParseNoWildSubstCheckPtr0(aRequest,aRequest->Src()); + if (r!=KErrNone) + return(r); + r=PathCheck(aRequest,aRequest->Src().FullName().Mid(2),&KCapFsSysFileCreate,&KCapFsPriFileCreate,&KCapFsROFileCreate, __PLATSEC_DIAGNOSTIC_STRING("Create File")); + if (r!=KErrNone) + return(r); + if (OpenOnDriveZOnly) + { + aRequest->SetDrive(&TheDrives[EDriveZ]); + aRequest->SetSubstedDrive(NULL); + } + return(r); + } + + + +TInt TFsFileReplace::DoRequestL(CFsRequest* aRequest) +// +// +// + { + __PRINT(_L("TFsFileReplace::DoRequestL(CFsRequest* aRequest)")); + return FsFileOpenL(aRequest, EFileReplace); + } + +TInt TFsFileReplace::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r=ParseNoWildSubstCheckPtr0(aRequest,aRequest->Src()); + if (r!=KErrNone) + return(r); + r=PathCheck(aRequest,aRequest->Src().FullName().Mid(2),&KCapFsSysFileReplace,&KCapFsPriFileReplace,&KCapFsROFileReplace, __PLATSEC_DIAGNOSTIC_STRING("Replace File")); + if (r!=KErrNone) + return(r); + + if (OpenOnDriveZOnly) // Yuck! yet another global + { + aRequest->SetDrive(&TheDrives[EDriveZ]); + aRequest->SetSubstedDrive(NULL); + } + return(r); + } + + +#ifdef __REMOVE_PLATSEC_DIAGNOSTIC_STRINGS__ + +#define __PLATSEC_DIAGNOSTIC_MESSAGE(s) NULL +static const TInt KMsgBuffSize = KMaxPath; + +#else + + #if defined(_UNICODE) && !defined(__KERNEL_MODE__) + +static const TInt KCharMsgMaxLen = KMaxPath - 1; +static const TInt KMsgBuffSize = KMaxPath; + + #else + +static const TInt KCharMsgMaxLen = 50; +static const TInt KMsgBuffSize = KMaxPath + KMsgMaxLen + 1; + + #endif // #if defined(_UNICODE) && !defined(__KERNEL_MODE__) + +// Local function to format a message +static +const char* FmtPlatSecMessage(TBufC& buff, CFsRequest& req, const char* str) + { + char* p = (char*)buff.Ptr(); + const char* const base = p; + // copy message string (if any) + if(str) + { + while(*str && p < &base[KCharMsgMaxLen - 2]) // 2 for trailing ": " + *p++ = *str++; + *p++ = ':'; + *p++ = ' '; + } + // append filename + const TDesC& fname = req.Src().FullName(); + const TInt end = Min(fname.Length(), + KMsgBuffSize * sizeof(*buff.Ptr()) - (p - base) - 1); + for(TInt i = 0; i < end; ++i) + *p++ = (char)fname[i]; + *p = 0; + return base; + } + +#define __PLATSEC_DIAGNOSTIC_MESSAGE(s) FmtPlatSecMessage(thisPath, *aRequest, s) + +#endif // #ifdef __REMOVE_PLATSEC_DIAGNOSTIC_STRINGS__ + + +TInt TFsFileOpen::Initialise(CFsRequest* aRequest) +// +// Parse and execute FileOpen service otherwise sets flag for +// asynchronous service +// + { + TInt r=ParseNoWildSubstCheckPtr0(aRequest,aRequest->Src()); + if (r!=KErrNone) + return(r); + + TBufC thisPath(aRequest->Src().FullName().Mid(2)); + TUint32 mode = (aRequest->Message().Int1() & ~(EFileStreamText | EFileReadAsyncAll | EFileBigFile)); + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) +// see if file is on our "Most Wanted" list + TInt errorCode; + checkCorruptNamesList(aRequest,errorCode); + if(errorCode!=KErrNone) + { + return errorCode; + } +#endif + + CFsMessageRequest* msgRequest = (CFsMessageRequest*)aRequest; + if (OpenOnDriveZOnly) + { + aRequest->SetDrive(&TheDrives[EDriveZ]); + aRequest->SetSubstedDrive(NULL); + } + + if(msgRequest->IsPluginRequest()) + { + // Always allow plugins to open files, regardless of the clients policy + return KErrNone; + } + + if(ComparePrivate(thisPath)) + { + if(! SIDCheck(aRequest,thisPath)) + { + if(!KCapFsPriFileOpen.CheckPolicy(aRequest->Message(), __PLATSEC_DIAGNOSTIC_MESSAGE("File Open in private path"))) + return KErrPermissionDenied; + } + } + else if(CompareResource(thisPath)) + { + if(mode != EFileShareReadersOrWriters && mode != EFileShareReadersOnly && mode != EFileRead) + // File opening mode EFileShareReadersOrWriters|EFileRead will fail the above test and not + // be checked for policy, whereas file opening mode EFileShareReadersOrWriters|EFileWrite + // will pass the test and will be checked for policy. + // EFileRead is 0 whereas EFileWrite is non 0. + { + if(!KCapFsROFileOpenWr.CheckPolicy(aRequest->Message(), __PLATSEC_DIAGNOSTIC_MESSAGE("File Open in resource path"))) + return KErrPermissionDenied; + } + } + else if(CompareSystem(thisPath)) + { + if(!(mode & EFileShareReadersOnly) && (mode & EFileWrite)) + { + if(!KCapFsSysFileOpenWr.CheckPolicy(aRequest->Message(), __PLATSEC_DIAGNOSTIC_MESSAGE("File Open in system path"))) + return KErrPermissionDenied; + } + else + { + if(!KCapFsSysFileOpenRd.CheckPolicy(aRequest->Message(), __PLATSEC_DIAGNOSTIC_MESSAGE("File Open in system path"))) + return KErrPermissionDenied; + } + } + + return(r); + } + +#undef __PLATSEC_DIAGNOSTIC_MESSAGE + +TInt TFsIsFileOpen::DoRequestL(CFsRequest* aRequest) +// +// Return whether a file is open or not +// + { + + __PRINT(_L("TFsIsFileOpen::DoRequestL(CFsRequest* aRequest)")); + CFileCB* file; + TInt r=aRequest->Drive()->IsFileOpen(aRequest->Src().FullName().Mid(2),file); + if (r!=KErrNone) + return(r); + TBool isOpen = file?(TBool)ETrue:(TBool)EFalse; + TPtrC8 pA((TUint8*)&isOpen,sizeof(TBool)); + aRequest->WriteL(KMsgPtr1,pA); + return(KErrNone); + } + +TInt TFsIsFileOpen::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r=ParseNoWildSubstCheckPtr0(aRequest,aRequest->Src()); + if (r!=KErrNone) + return(r); + r=PathCheck(aRequest,aRequest->Src().FullName().Mid(2),&KCapFsSysIsFileOpen,&KCapFsPriIsFileOpen, __PLATSEC_DIAGNOSTIC_STRING("Is File Open")); + return(r); + } + + +TInt TFsListOpenFiles::DoRequestL(CFsRequest* aRequest) +// +// List open files +// + { + + __PRINT(_L("TFsListOpenFiles::DoRequestL(CFsRequest* aRequest)")); + + TOpenFileListPos listPos; + TPckg listPkg(listPos); + aRequest->ReadL(KMsgPtr0,listPkg); + TBuf8 entryArray(0); + + TThreadId idClient; + TPckgC id(idClient); + + CSessionFs* session; + TBool fileFound=(listPos.iEntryListPos) ? (TBool)ETrue : EFalse; + TInt entryListPos; + TInt count; +Start: + FOREVER + { + session=(*TheFileServer)[listPos.iSession]; //this global may not be the best way AJ + if (session==NULL) + goto End; + session->Handles().Lock(); + count=session->Handles().Count(); + if (count) + break; + session->Handles().Unlock(); + listPos.iSession++; + } + + entryListPos=listPos.iEntryListPos; + while (entryListPosHandles()[entryListPos]; + if (obj==NULL || obj->UniqueID()!=FileShares->UniqueID()) + { + entryListPos++; + continue; // Is not a CFileShare + } + CFileCB& fileCb=((CFileShare*)obj)->File(); + + TEntry fileEntry; + // Set kEntryAttPacked to indicate it is in packed form + fileEntry.iAtt=fileCb.Att() | KEntryAttPacked; + TInt64 fileSize = fileCb.Size64(); + fileEntry.iSize = I64LOW(fileSize); + fileEntry.iModified=fileCb.Modified(); + fileEntry.iName=fileCb.FileName(); + + // Pack - Copy iSizeHigh and reset iReserved in packed form + TUint32* pSizeHigh = PtrAdd((TUint32*)&fileEntry, EntrySize(fileEntry, EFalse)); + + *pSizeHigh++ = I64HIGH(fileSize); // Copy iSizeHigh + *pSizeHigh = 0; // Reset iReserved + + TInt entrySize=EntrySize(fileEntry, ETrue); + if (entryArray.Length()+entrySize>entryArray.MaxLength()) + break; + TPtrC8 pfileEntry((TUint8*)&fileEntry,entrySize); + entryArray.Append(pfileEntry); + entryListPos++; + } + idClient = session->ThreadId(); + session->Handles().Unlock(); + + if (entryArray.Length()==0) + listPos.iSession++; + if (fileFound==EFalse && entryArray.Length()==0) + goto Start; + listPos.iEntryListPos=entryListPos; + +End: + aRequest->WriteL(KMsgPtr1,id); + aRequest->WriteL(KMsgPtr0,listPkg); + aRequest->WriteL(KMsgPtr2,entryArray); + return(KErrNone); + } + +TInt TFsListOpenFiles::Initialise(CFsRequest* /*aRequest*/) +// +// +// + { + return KErrNone; + } + +LOCAL_C void FsFileTempFinishL(CFsRequest* aRequest,TFileName& aN,TInt aH) + { + + aRequest->WriteL(KMsgPtr2,aRequest->Src().Drive()); + aRequest->WriteL(KMsgPtr2,aN,2); + TPtrC8 pH((TUint8*)&aH,sizeof(TInt)); + aRequest->WriteL(KMsgPtr3,pH); + } + +TInt TFsFileTemp::DoRequestL(CFsRequest* aRequest) +// +// Create a temporary file. +// + { + __PRINT(_L("TFsFileTemp::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + TFileName n; + TInt h; + r=aRequest->Drive()->FileTemp(aRequest,h,aRequest->Src().FullName().Mid(2),n,aRequest->Message().Int1()); + if (r!=KErrNone) + return(r); + + TRAP(r, FsFileTempFinishL(aRequest,n,h)) + CheckForLeaveAfterOpenL(r,aRequest,h); + aRequest->Session()->IncResourceCount(); + + return(KErrNone); + } + +TInt TFsFileTemp::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r=ParseNoWildSubstPtr0(aRequest,aRequest->Src()); + if (r!=KErrNone) + return(r); + r=PathCheck(aRequest,aRequest->Src().FullName().Mid(2),&KCapFsSysFileTemp,&KCapFsPriFileTemp,&KCapFsROFileTemp, __PLATSEC_DIAGNOSTIC_STRING("Temp File")); + if (r!=KErrNone) + return(r); + if (aRequest->Src().NameOrExtPresent()) + return(KErrBadName); + return(r); + } + + +TInt TFsFileRead::DoRequestL(CFsRequest* aRequest) +// +// Read from a file. +// + { + + __PRINT(_L("TFsFileRead::DoRequestL(CFsRequest* aRequest)")); + __PRINT1(_L("aRequest->Session() = 0x%x"),aRequest->Session()); + + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + __ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(EBadOperationIndex)); + TMsgOperation& currentOperation = msgRequest.CurrentOperation(); + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + TInt r = file->CheckMount(); + __PRINT1(_L("share->CheckMount() returned = %d"),r); + if (r!=KErrNone) + return(r); + + TInt& len = currentOperation.iReadWriteArgs.iLength; + TInt& totalLen = currentOperation.iReadWriteArgs.iTotalLength; + TInt64 pos = currentOperation.iReadWriteArgs.iPos; + TInt& offset = currentOperation.iReadWriteArgs.iOffset; + + // Fair scheduling - + // Needs extended file API to work (so that we can sepcify an offset) + // Also needs a separate drive thread to prevent excessive stack usage + len = Min(len, totalLen); + if (file->ExtendedFileInterfaceSupported() && !FsThreadManager::IsDriveSync(aRequest->DriveNumber(), EFalse)) + { + len = Min(len, file->FairSchedulingLen()); + } + if (pos == KCurrentPosition64) + pos = share->iPos; + + currentOperation.iReadWriteArgs.iPos = pos; + + __ASSERT_DEBUG(len > 0, Fault(EInvalidReadLength)); + __ASSERT_DEBUG(len <= totalLen, Fault(EInvalidReadLength)); + + // The mount and any extensions must all support local buffers in order to support + // internally generated requests (ie - requests originating from plugins) + if ((aRequest->Message().Handle() == KLocalMessageHandle || !currentOperation.iClientRequest) && !file->LocalBufferSupport()) + { + r = KErrNotSupported; + } + + TInt reqLen = len; + if(r == KErrNone) + { + if (currentOperation.iClientRequest) + { + // Current operation points to a descriptor + // The request originated from a client (with a normal message handle) or a plugin (KLocalMessageHandle) + TRAP(r,file->ReadL(pos, len, (TPtr8*) aRequest->Message().Ptr0(), aRequest->Message(), offset)) + } + else + { + // Current operation points to a local buffer + // The request originated from the file server (e.g. file cache) with a local message handle (KLocalMessageHandle) + TPtr8 dataDesc((TUint8*) currentOperation.iReadWriteArgs.iData + currentOperation.iReadWriteArgs.iOffset, len, len); + const RLocalMessage msg; + TRAP(r,file->ReadL(pos, len, &dataDesc, msg, 0)); + } + } + + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + CCacheManager* manager = CCacheManagerFactory::CacheManager(); + if (manager) + { + manager->Stats().iUncachedPacketsRead++; + manager->Stats().iUncachedBytesRead+= len; + } +#endif + +//RDebug::Print(_L("ReadR: req %08X pos %ld\t len %d file %08X\n"), aRequest, pos, len, file); + + +#if defined (_DEBUG_READ_AHEAD) + RDebug::Print(_L("ReadR: req %08X pos %ld\t len %d nextPos %ld file %08X\n"), aRequest, pos, len, pos + len, file); +#endif + + offset+= len; + currentOperation.iReadWriteArgs.iPos+= len; + totalLen-= reqLen; + + + // update the file share's position IF this request came from the client + if (share && r==KErrNone && currentOperation.iClientRequest) + { + __e32_atomic_store_ord64(&share->iPos, pos + len); + } + + // re-issue request if not complete (to support fair scheduling) + if (r == KErrNone && totalLen > 0) + return CFsRequest::EReqActionBusy; // dispatch request again to back of queue + + return(r); + } + + +TInt TFsFileRead::Initialise(CFsRequest* aRequest) +// +// +// + { + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + + TInt r = DoInitNoParse(aRequest); + if (r != KErrNone) + return r; + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + TMsgOperation* msgOp = msgRequest.CurrentOperationPtr(); + if (!msgOp) // initialised already ? + { + r = msgRequest.PushOperation(TFsFileRead::Complete); + if (r != KErrNone) + return r; + msgOp = msgRequest.CurrentOperationPtr(); + } + // try to serialize requests to prevent asynchronous requests being processed out of sequence - + // this isn't possible if a plugin is loaded as this may issue it's own requests + if (!share->RequestStart(&msgRequest)) + return CFsRequest::EReqActionPending; + + TDrive& drive = share->File().Drive(); + + TInt64 pos, reqPos; + TInt len, reqLen; + + reqLen = len = aRequest->Message().Int1(); + + if(aRequest->IsDescData(KMsgPtr2)) + {//-- 64-bit file addressing, absolute read position is TInt64 + TPckg pkPos(reqPos); + aRequest->ReadL(KMsgPtr2, pkPos); + } + else + { + if(aRequest->Message().Int2() == (TInt)I64LOW(KCurrentPosition64)) + reqPos = KCurrentPosition64; // Position is KCurrentPosition64 (-1) + else //-- legacy, RFile addressing + reqPos = MAKE_TINT64(0,aRequest->Message().Int2()); // Position is absolute value < 4GB, it's TUint + } + + msgOp->iClientPosition = pos = reqPos; + + if (len < 0) + return KErrArgument; + + if (len == 0) + return CFsRequest::EReqActionComplete; + + if (pos == KCurrentPosition64) + pos = share->iPos; + + const TInt64 fileSize = file->CachedSize64(); + if (pos > fileSize) + pos = fileSize; + + if ((r = file->CheckLock64(share,pos,len)) != KErrNone) + return r; + + + TDes8* pD = (TDes8*) aRequest->Message().Ptr0(); + + if((share->iMode & EFileReadAsyncAll) && (aRequest->Message().ClientStatus() != NULL)) + { + drive.Lock(); + if (pos + len > fileSize) + { + r = share->File().AddAsyncReadRequest(share, reqPos, reqLen, aRequest); + drive.UnLock(); + return (r == KErrNone)?CFsRequest::EReqActionComplete:r; + } + drive.UnLock(); + } + + if (pos == fileSize) + { + __e32_atomic_store_ord64(&share->iPos, pos); + r = aRequest->Write(KMsgPtr0, KNullDesC8); + return(r == KErrNone?CFsRequest::EReqActionComplete:r); + } + + if (pos + len > fileSize) + { + // filesize - pos shall of TInt size + // Hence to suppress warning + len = (TInt)(fileSize - pos); + } + + msgOp->Set(pos, len, (TDesC8*) pD); + +//RDebug::Print(_L("ReadI: req %08X pos %ld\t len %d file %08X\n"), aRequest, pos, len, file); + +#if defined (_DEBUG_READ_AHEAD) + RDebug::Print(_L("ReadI: req %08X pos %ld\t len %d file %08X\n"), aRequest, pos, len, file); +#endif + + return KErrNone; + } + +TInt TFsFileRead::PostInitialise(CFsRequest* aRequest) + { + CFileShare* share = (CFileShare*) aRequest->ScratchValue(); + CFileCB* file = &share->File(); + TInt r = KErrNone; + + CFileCache* fileCache = file->FileCache(); + if (fileCache) + { + r = fileCache->ReadBuffered(*(CFsMessageRequest*)aRequest, share->iMode); + + // if we're not reading from cache, force read ahead position & length to be recalculated + if (r == KErrNone) + fileCache->ResetReadAhead(); + } + + return r; + } + +TInt TFsFileRead::Complete(CFsRequest* aRequest) + { +// RDebug::Print(_L("TFsFileRead::Complete() aRequest %08X"), aRequest); + + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + // Flag the request as having ended to allow another async read to occur + share->RequestEnd(&msgRequest); + + + // issue read-ahead + CFileCache* fileCache = file->FileCache(); + if (fileCache && msgRequest.LastError() == KErrNone) + fileCache->ReadAhead(msgRequest, share->iMode); + + return CFsRequest::EReqActionComplete; + } + + +void GetFileFromScratch(CFsRequest* aRequest, CFileShare*& aShare, CFileCB*& aFile) + { + + TInt64 scratchValue = aRequest->ScratchValue64(); + TBool scratchValueIsShare I64HIGH(scratchValue); + TUint32 scratchValueLow = I64LOW(scratchValue); + + aShare = NULL; + aFile = NULL; + + if (scratchValueIsShare) + { + aShare = (CFileShare*) scratchValueLow; + if (aShare) + aFile = &aShare->File(); + } + else + { + aFile = (CFileCB*) scratchValueLow; + } + } + +/** + Common init preamble for TFsFileWrite::Initialise() and TFsFileWrite::DoRequestL() + + @param aShare pointer to the file share + @param aFile pointer to the file object this function is called for + @param aPos file position to write data. Note that it can be KCurrentPosition64 i.e. KMaxTUint64 + @param aLen length of the data to write + @param aFileSize current file size + @param aFsOp File Server message code. See TFsMessage. It must be ether EFsFileWrite for normal write, or EFsFileWriteDirty when file cache flushes dirty data +*/ + +TInt TFsFileWrite::CommonInit(CFileShare* aShare, CFileCB* aFile, TInt64& aPos, TInt& aLen, TInt64 aFileSize, TFsMessage aFsOp) + { + + if (aShare && aPos==KCurrentPosition64) + {//-- write to the current position in the file + aPos = aShare->iPos; + } + + if(aPos > aFileSize) + aPos = aFileSize; + + //-- check that the new position won't exceed maximum file size + { + const TUint64 endPos = aPos+aLen; + + //-- Large file mode check. Legacy RFile size can't exceed 2G-1 + if(aShare && !(aShare->IsFileModeBig()) && (endPos > KMaxLegacyFileSize)) + return KErrTooBig; + + //-- check CMountCB limitation on maximum file size + if(endPos > aFile->MaxSupportedSize()) + return KErrNotSupported; //-- this is for the sake of error codes consistency; current FSYs return + //-- this code in the case of accessing a file beyond its limit + } + + if (aShare) + { + TInt r; + if ((r=aFile->CheckLock64(aShare,aPos,aLen))!=KErrNone) + return(r); + } + + ASSERT(aFsOp == EFsFileWrite || aFsOp == EFsFileWriteDirty); + if(aFsOp == EFsFileWrite) + {//-- this call is originated from explicit file write operation. Set 'Archive' attribute and new file time. + aFile->SetArchiveAttribute(); //-- it will also set KEntryAttModified + } + else + {//-- don't touch data and attributes if it is cache flushing dirty data + aFile->iAtt |= KEntryAttModified; + } + + + return KErrNone; + } + +void TFsFileWrite::CommonEnd(CFsMessageRequest* aMsgRequest, TInt aRetVal, TUint64 aInitSize, TUint64 aNewSize, TInt64 aNewPos, TBool aFileWrite) +// +// Common end for TFsFileWrite::DoRequestL() and CFileCache::WriteBuffered() +// + { + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aMsgRequest, share, file); + CFileCache* fileCache = file->FileCache(); + ASSERT(aFileWrite || fileCache); + + TMsgOperation& currentOperation = aMsgRequest->CurrentOperation(); + + if (aRetVal == KErrNone || aRetVal == CFsRequest::EReqActionComplete) + { + if (share) + { + __e32_atomic_store_ord64(&share->iPos, aNewPos); + } + + if ((TUint64)aNewPos > aNewSize) + { + if(aFileWrite) + file->SetSize64(aNewPos, EFalse); + else + fileCache->SetSize64(aNewPos); + aNewSize = aNewPos; + } + + // ensure cached file is at least as big as uncached file + if (fileCache && fileCache->Size64() < aNewPos) + { + file->SetCachedSize64(aNewPos); + } + + // Service async reads if the file has grown & this is the last fair-scheduled request + + // If the file has grown, flag this and call CFileCB::NotifyAsyncReaders() + // later in TFsFileWrite::Complete() - we need to delay the call because + // CFileCB::NotifyAsyncReaders() may requeue a request which will cause + // the drive thread to spin because this file share is still marked as in use + // (by CFileShare::RequestStart()) + if((aNewSize > aInitSize) && (currentOperation.iReadWriteArgs.iTotalLength == 0)) + { + file->SetNotifyAsyncReadersPending(ETrue); + } + + file->iAtt |= KEntryAttModified; + + } + else if (aRetVal == KErrCorrupt) + file->SetFileCorrupt(ETrue); + else if (aRetVal == KErrBadPower || (aRetVal == KErrAbort && !PowerOk())) + file->SetBadPower(ETrue); + + file->ResetReadAhead(); + aMsgRequest->SetFreeChanged(aNewSize != aInitSize); + } + +TInt TFsFileWrite::DoRequestL(CFsRequest* aRequest) +// +// Write to a file. +// + { + __PRINT(_L("TFsFileWrite::DoRequestL(CFsRequest* aRequest)")); + + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + __ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(EBadOperationIndex)); + TMsgOperation& currentOperation = msgRequest.CurrentOperation(); + + TInt r; + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + TInt64 initSize = file->Size64(); + + r = file->CheckMount(); + if (r!=KErrNone) + return(r); + + + TInt& len = currentOperation.iReadWriteArgs.iLength; + + // Fair scheduling - + // Needs extended file API to work (so that we can specify an offset) + // Also needs a separate drive thread to prevent excessive stack usage + len = Min(len, currentOperation.iReadWriteArgs.iTotalLength); + if (file->ExtendedFileInterfaceSupported() && !FsThreadManager::IsDriveSync(aRequest->DriveNumber(), EFalse)) + { + len = Min(len, file->FairSchedulingLen()); + } + + __ASSERT_DEBUG(len <= currentOperation.iReadWriteArgs.iTotalLength, Fault(EInvalidWriteLength)); + + + const TFsMessage fsOp = (TFsMessage)aRequest->Operation()->Function(); + r = CommonInit(share, file, currentOperation.iReadWriteArgs.iPos, len, initSize, fsOp); + + if (r != KErrNone) + return r; + + TInt64 pos = currentOperation.iReadWriteArgs.iPos; + + TInt64 upos = pos+len; + if (upos > initSize) + { + r = CheckDiskSpace(upos - initSize, aRequest); + if(r != KErrNone) + return r; + } + + // The mount and any extensions must all support local buffers in order to support + // internally generated requests (ie - requests originating from plugins) + if ((aRequest->Message().Handle() == KLocalMessageHandle || !currentOperation.iClientRequest) && !file->LocalBufferSupport()) + { + r = KErrNotSupported; + } + + if(r == KErrNone) + { + if (currentOperation.iClientRequest) + { + TRAP(r,file->WriteL(pos, len, (const TPtrC8*) aRequest->Message().Ptr0(), aRequest->Message(), currentOperation.iReadWriteArgs.iOffset)) + } + else + { + TPtr8 dataDesc((TUint8*) currentOperation.iReadWriteArgs.iData + currentOperation.iReadWriteArgs.iOffset, len, len); + const RLocalMessage msg; + TRAP(r,file->WriteL(pos, len, &dataDesc, msg, 0)); + } + } + +//RDebug::Print(_L("WriteR: req %08X pos %ld\t len %d file %08X\n"), aRequest, pos, len, file); + +#if defined(_DEBUG) || defined(_DEBUG_RELEASE) + CCacheManager* manager = CCacheManagerFactory::CacheManager(); + if (manager) + { + manager->Stats().iUncachedPacketsWritten++; + manager->Stats().iUncachedBytesWritten+= len; + } +#endif + + if (r == KErrNone) + { + // update position, offset & length remaining + currentOperation.iReadWriteArgs.iOffset+= len; + currentOperation.iReadWriteArgs.iPos+= len; + currentOperation.iReadWriteArgs.iTotalLength-= len; + } + TUint64 currentSize = MAKE_TUINT64(file->iBody->iSizeHigh,file->iSize); + CommonEnd(&msgRequest, r, initSize, currentSize, pos+len, ETrue); + + // re-issue request if not complete (to support fair scheduling) + if (r == KErrNone && currentOperation.iReadWriteArgs.iTotalLength > 0) + return CFsRequest::EReqActionBusy; // dispatch request again to back of queue + + return(r); + } + +TInt TFsFileWrite::Initialise(CFsRequest* aRequest) +// +// +// + { + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + + TInt r = DoInitNoParse(aRequest); + if (r != KErrNone) + return r; + + // If the drive has been dismounted, don't even attempt to write to the file as that + // will create dirty data which can then not be flushed without hitting ASSERTs etc. + if (!FsThreadManager::IsDriveAvailable(aRequest->DriveNumber(), EFalse)) + return KErrNotReady; + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + // Bail out if there's a new mount which isn't ours - this would fail + // later on anyway when TFsFileWrite::DoRequestL() called CFileCB::CheckMount() + if ( !file->Drive().IsCurrentMount(file->Mount()) ) + return KErrDisMounted; + + + TMsgOperation* msgOp = msgRequest.CurrentOperationPtr(); + if (!msgOp) // initialised already ? + { + r = msgRequest.PushOperation(TFsFileWrite::Complete); + if (r != KErrNone) + return r; + msgOp = msgRequest.CurrentOperationPtr(); + } + // try to serialize requests to prevent asynchronous requests being processed out of sequence - + // this isn't possible if a plugin is loaded as this may issue it's own requests + if (share && !share->RequestStart(&msgRequest)) + return CFsRequest::EReqActionPending; + + TInt64 pos; + + if(aRequest->IsDescData(KMsgPtr2)) + {//-- 64-bit file addressing, absolute read position is TInt64 + TPckg pkPos(pos); + aRequest->ReadL(KMsgPtr2, pkPos); + } + else + { + if(aRequest->Message().Int2() == (TInt)I64LOW(KCurrentPosition64)) + pos = KCurrentPosition64;// Position is KCurrentPosition64 (-1) + else + pos = MAKE_TINT64(0,aRequest->Message().Int2());// Position is absolute value < 4GB, it's a TUint type + } + + msgOp->iClientPosition = pos; + TInt len = aRequest->Message().Int1(); + + TDesC8* pD = (TDes8*) aRequest->Message().Ptr0(); + + if (len == 0) + return CFsRequest::EReqActionComplete; + + if (len < 0) + return KErrArgument; + + //if this request was sent down from a plugin, then we want to + //ignore the mode that the files was opened in. + if (share && !msgRequest.IsPluginRequest()) + { + if ((share->iMode & EFileWrite)==0 || + (share->iMode & KFileShareMask) == EFileShareReadersOnly) + { + return(KErrAccessDenied); + } + } + + + const TFsMessage fsOp = (TFsMessage)aRequest->Operation()->Function(); + r = CommonInit(share, file, pos, len, file->CachedSize64(), fsOp); + if (r != KErrNone) + return r; + + msgOp->Set(pos, len, pD); + + return KErrNone; + } + + +TInt TFsFileWrite::PostInitialise(CFsRequest* aRequest) + { + CFileShare* share = (CFileShare*) aRequest->ScratchValue(); + CFileCB* file = &share->File(); + TInt r = KErrNone; + + CFileCache* fileCache = file->FileCache(); + if (fileCache) + { + // If there's no data left proceed straight to completion stage + // This can happen when re-posting a completed write request to + // start the dirty data timer + if (((CFsMessageRequest*) aRequest)->CurrentOperation().iReadWriteArgs.iTotalLength == 0) + return CFsRequest::EReqActionComplete; + + r = fileCache->WriteBuffered(*(CFsMessageRequest*)aRequest, share->iMode); + } + + return r; + } + +TInt TFsFileWrite::Complete(CFsRequest* aRequest) + { + CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest; + + + CFileShare* share; + CFileCB* file; + GetFileFromScratch(aRequest, share, file); + + if (share) + share->RequestEnd(&msgRequest); + + if (file->NotifyAsyncReadersPending()) + file->NotifyAsyncReaders(); + + return CFsRequest::EReqActionComplete; + } + +TInt TFsFileLock::DoRequestL(CFsRequest* aRequest) +// +// Lock a region of the file. +// + { + + __PRINT(_L("TFsFileLock::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + + // We must wait for ALL shares to have no active requests (see RequestStart()) + // and post this request to the back of the queue if there are any. This is to + // avoid a fair-scheduled write from writing to a locked region of a file + CSessionFs* session = aRequest->Session(); + if (session) + { + TBool fileInUse = EFalse; + session->Handles().Lock(); + TInt count = session->Handles().Count(); + CFileCB* file = &share->File(); + + for (TInt n=0; nHandles()[n]; + if (obj != NULL && + obj->UniqueID() == FileShares->UniqueID() && + (file == &((CFileShare*) obj)->File()) && + ((CFileShare*) obj)->RequestInProgress()) + { + CFsMessageRequest& msgRequest = *(CFsMessageRequest*)aRequest; + if(msgRequest.IsPluginRequest()) + break; + + fileInUse = ETrue; + break; + } + } + session->Handles().Unlock(); + if (fileInUse) + return CFsRequest::EReqActionBusy; + } + + TInt64 pos, length; + if(aRequest->IsDescData(KMsgPtr0)) + { + TPckg pkPos(pos); + aRequest->ReadL(KMsgPtr0, pkPos); + } + else + { + pos = MAKE_TINT64(0, aRequest->Message().Int0()); + } + + if(aRequest->IsDescData(KMsgPtr1)) + { + TPckg pkLength(length); + aRequest->ReadL(KMsgPtr1, pkLength); + if(length <= 0) + User::Leave(ELockLengthZero); + } + else + { + length = aRequest->Message().Int1(); + if(length <= 0) + User::Leave(ELockLengthZero); + } + return(share->File().AddLock64(share, pos, length)); + } + +TInt TFsFileLock::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileUnlock::DoRequestL(CFsRequest* aRequest) +// +// Unlock a region of the file. +// + { + __PRINT(_L("TFsFileUnlock::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + + TInt64 pos, length; + + if(aRequest->IsDescData(KMsgPtr0)) + { + TPckg pkPos(pos); + aRequest->ReadL(KMsgPtr0, pkPos); + } + else + { + pos = MAKE_TINT64(0, aRequest->Message().Int0()); + } + + if(aRequest->IsDescData(KMsgPtr1)) + { + TPckg pkLength(length); + aRequest->ReadL(KMsgPtr1, pkLength); + if(length <= 0) + User::Leave(EUnlockLengthZero); + } + else + { + length = aRequest->Message().Int1(); + if(length <= 0) + User::Leave(EUnlockLengthZero); + } + return(share->File().RemoveLock64(share, pos, length)); + } + +TInt TFsFileUnlock::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileSeek::DoRequestL(CFsRequest* aRequest) +// +// Set the file position. +// + { + + __PRINT(_L("TFsFileSeek::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TInt64 size = share->File().CachedSize64(); + TInt64 pos; + if(aRequest->IsDescData(KMsgPtr0)) + { + TPckg pkPos(pos); + aRequest->ReadL(KMsgPtr0, pkPos); + } + else + { + pos = aRequest->Message().Int0(); + } + + TInt r,t; + + if (share->iPos != pos) + share->File().ResetReadAhead(); + + switch (aRequest->Message().Int1()) + { + case ESeekCurrent: + pos+=share->iPos; + if (pos<0) + pos=0; + + if (pos>size) + pos=size; + + // Large file mode check + if((!(share->IsFileModeBig())) && ((TUint64)pos > KMaxLegacyFileSize)) + return (KErrTooBig); + + break; + case ESeekEnd: + pos+=size; + if (pos<0) + pos=0; + if (pos>size) + pos=size; + // Large file mode check + if((!(share->IsFileModeBig())) && ((TUint64)pos > KMaxLegacyFileSize)) + return (KErrTooBig); + + break; + case ESeekAddress: + t = (TUint)pos; + r = share->File().Address(t); + pos = (TUint)t; + if(KErrNone != r) + return(r); + goto writeBackPos; + case ESeekStart: + if (pos>=0) + { + share->iPos = pos; + return KErrNone; + } + return(KErrArgument); + default: + return(KErrArgument); + } + __e32_atomic_store_ord64(&share->iPos, pos); +writeBackPos: + TPckgC pkNewPos(pos); + aRequest->WriteL(KMsgPtr2, pkNewPos); + return(KErrNone); + } + +TInt TFsFileSeek::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileFlush::DoRequestL(CFsRequest* aRequest) +// +// Commit any data in memory to the media. +// + { + + __PRINT(_L("TFsFileFlush::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + + // if any write requests are being fair scheduled, wait for them to complete + if (share->RequestInProgress()) + return CFsRequest::EReqActionBusy; + + // flush the write cache + TInt r; + CFileCache* fileCache = share->File().FileCache(); + if (fileCache && (r = fileCache->FlushDirty(aRequest)) != CFsRequest::EReqActionComplete) + { + //To be used in notification framework + //CFsMessageRequest& msgRequest = (CFsMessageRequest&)*aRequest; + //msgRequest.iUID = msgRequest.Message().Identity(); + return r; + } + + if ((share->File().Att()&KEntryAttModified)==0) + return(KErrNone); + if ((share->iMode&EFileWrite)==0) + return(KErrAccessDenied); + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECFileCBFlushDataL, EF32TraceUidFileSys, &share->File()); + TRAP(r,share->File().FlushDataL()); + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECFileCBFlushDataLRet, EF32TraceUidFileSys, r); + + return(r); + } + +TInt TFsFileFlush::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileSize::DoRequestL(CFsRequest* aRequest) +// +// Get the file size. +// + { + + __PRINT(_L("TFsFileSize::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TInt64 size = share->File().CachedSize64(); + // Large file mode check and error handling is done at client library side. + // Return file size even if it is greater than 2GB - 1. + TPckgC pkSize(size); + aRequest->WriteL(KMsgPtr0, pkSize); + return(KErrNone); + } + +TInt TFsFileSize::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileSetSize::DoRequestL(CFsRequest* aRequest) +// +// Set the file size. +// + { + __PRINT(_L("TFsFileSetSize::DoRequestL(CFsRequest* aRequest)")); + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + if ((share->iMode&EFileWrite)==0) + return(KErrAccessDenied); + TInt r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + TInt64 size; + if(aRequest->IsDescData(KMsgPtr0)) + { + TPckg pkSize(size); + aRequest->ReadL(KMsgPtr0, pkSize); + } + else + { + size = aRequest->Message().Int0(); + } + + if(size < 0) + User::Leave(ESizeNegative); + + // Large file mode check + if((!(share->IsFileModeBig())) && ((TUint64)size > KMaxLegacyFileSize)) + return (KErrTooBig); + + CFileCB& file=share->File(); + + // flush the write cache + CFileCache* fileCache = share->File().FileCache(); + if (fileCache && (r = fileCache->FlushDirty(aRequest)) != CFsRequest::EReqActionComplete) + return r; + + if (size==file.Size64()) + return(KErrNone); + + TBool fileHasGrown = size > file.Size64(); + if (fileHasGrown) + { + r = CheckDiskSpace(size - file.Size64(), aRequest); + if(r != KErrNone) + return r; + + r=file.CheckLock64(share,file.Size64(),size-file.Size64()); + } + else + r=file.CheckLock64(share,size,file.Size64()-size); + if (r!=KErrNone) + return(r); + __PRINT1(_L("Owner mount size before SetSize() = %d"),I64LOW(file.Mount().Size())); + TRAP(r,file.SetSizeL(size)) + if (r!=KErrNone) + { + // Set size failed + __PRINT1(_L("SetSize() failed = %d"),r); + __PRINT1(_L("Owner mount size now = %d"),I64LOW(file.Mount().Size())); + return(r); + } + file.SetArchiveAttribute(); + file.SetSize64(size, EFalse); + file.SetCachedSize64(size); + + if(fileHasGrown) + file.NotifyAsyncReaders(); // Service outstanding async reads if the file has grown + + return(r); + } + +TInt TFsFileSetSize::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileAtt::DoRequestL(CFsRequest* aRequest) +// +// Get the file attributes. +// + { + + __PRINT(_L("TFsFileAtt::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); +// TInt att=(TInt)aRequest->FileShare()->File().Att()&KEntryAttMaskSupported; + TInt att=(TInt)share->File().Att(); // DRM: let ROM XIP attribute through + TPtrC8 pA((TUint8*)&att,sizeof(TInt)); + aRequest->WriteL(KMsgPtr0,pA); + + return(KErrNone); + } + +TInt TFsFileAtt::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileSetAtt::DoRequestL(CFsRequest* aRequest) +// +// Set the file attributes. +// + { + __PRINT(_L("TFsFileSetAtt::DoRequestL(CSessionFs* aSession)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + if ((share->iMode&EFileWrite)==EFalse) + return(KErrAccessDenied); + + TUint setAttMask=(TUint)(aRequest->Message().Int0()); + TUint clearAttMask=(TUint)aRequest->Message().Int1(); + ValidateAtts(share->File().Att(),setAttMask,clearAttMask); + + TRACE5(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryL, EF32TraceUidFileSys, &share->File(), 0, 0, setAttMask,clearAttMask); + TRAP(r,share->File().SetEntryL(TTime(0),setAttMask,clearAttMask)) + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryLRet, EF32TraceUidFileSys, r); + + return(r); + } + + +TInt TFsFileSetAtt::Initialise(CFsRequest* aRequest) +// +// Call GetShareFromHandle to determine asynchronicity *** +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileModified::DoRequestL(CFsRequest* aRequest) +// +// Get the modified date and time. +// + { + __PRINT(_L("TFsFileModified::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TTime mod=share->File().Modified(); + TPtrC8 pM((TUint8*)&mod,sizeof(TTime)); + aRequest->WriteL(KMsgPtr0,pM); + + return(KErrNone); + } + + +TInt TFsFileModified::Initialise(CFsRequest* aRequest) +// +// Call GetShareFromHandle to determine asynchronicity *** +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileSetModified::DoRequestL(CFsRequest* aRequest) +// +// Set the modified date and time. +// + { + __PRINT(_L("TFsFileSetModified::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + if ((share->iMode&EFileWrite)==EFalse) + return(KErrAccessDenied); + + TTime time; + TPtr8 t((TUint8*)&time,sizeof(TTime)); + aRequest->ReadL(KMsgPtr0,t); + + TRACE5(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryL, EF32TraceUidFileSys, &share->File(), 0, 0, KEntryAttModified,0); + TRAP(r,share->File().SetEntryL(time,KEntryAttModified,0)) + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryLRet, EF32TraceUidFileSys, r); + + return(r); + } + + +TInt TFsFileSetModified::Initialise(CFsRequest* aRequest) +// +// Call GetShareFromHandle to determine asynchronicity *** +// + { + return(DoInitNoParse(aRequest)); + } + +TInt TFsFileSet::DoRequestL(CFsRequest* aRequest) +// +// Set the attributes and the modified date and time. +// + { + __PRINT(_L("TFsFileSet::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + if (share->File().Att()&KEntryAttReadOnly) + return(KErrAccessDenied); + + if ((share->iMode&EFileWrite)==0) + { + if(share->File().Drive().IsWriteProtected()) + return(KErrAccessDenied); + } + + TTime time; + TPtr8 t((TUint8*)&time,sizeof(TTime)); + aRequest->ReadL(KMsgPtr0,t); + TUint setAttMask=(TUint)(aRequest->Message().Int1()|KEntryAttModified); + TUint clearAttMask=(TUint)aRequest->Message().Int2(); + ValidateAtts(share->File().Att(),setAttMask,clearAttMask);// Validate attributes + + TRACE5(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryL, EF32TraceUidFileSys, &share->File(), 0, 0, setAttMask,clearAttMask); + TRAP(r,share->File().SetEntryL(time,setAttMask,clearAttMask)) + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetEntryLRet, EF32TraceUidFileSys, r); + + return(r); + } + +TInt TFsFileSet::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileChangeMode::DoRequestL(CFsRequest* aRequest) +// +// The access to a file may be changed from share exclusive to share readers only or vice versa. +// KErrAccessDenied is returned if the file has multiple readers. +// + { + __PRINT(_L("TFsFileChangeMode::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TInt r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + const TFileMode currentMode = (TFileMode)share->iMode; + const TFileMode newMode = (TFileMode)aRequest->Message().Int0(); + + // check argument + // (only EFileShareExclusive and EFileShareReadersOnly are allowed) + if((newMode & ~EFileShareReadersOnly) != 0) + return KErrArgument; + + // check if the file is in EFileShareAny mode + if( (currentMode & EFileShareAny) || + // or the file has been opened for writing in EFileShareExclusive mode, + // and an attempt is made to change the access mode to EFileShareReadersOnly + ((currentMode & EFileWrite) && + (currentMode & KFileShareMask) == EFileShareExclusive && + newMode == EFileShareReadersOnly) ) + return KErrAccessDenied; + + // check if an attempt is made to change the share mode to EFileShareExclusive + // while the file has multiple readers + if (newMode == EFileShareExclusive && (currentMode & KFileShareMask) != EFileShareExclusive) + { + // Check no other CFileCB is reading the file. + FileShares->Lock(); + TInt count=FileShares->Count(); + TBool found=EFalse; + while(count--) + { + CFileShare* fileShare=(CFileShare*)(*FileShares)[count]; + if (&fileShare->File()==&share->File()) + { + if (found) + { + FileShares->Unlock(); + return(KErrAccessDenied); + } + found=ETrue; + } + } + FileShares->Unlock(); + } + share->iMode&=~KFileShareMask; + share->iMode|=newMode; + share->File().SetShare(newMode); + return(KErrNone); + } + + +TInt TFsFileChangeMode::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r = DoInitNoParse(aRequest); + if(r != KErrNone) + return r; + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + if(CompareResource(share->File().FileName().Mid(2)) ) + { + if(!KCapFsFileChangeMode.CheckPolicy(aRequest->Message(), __PLATSEC_DIAGNOSTIC_STRING("File Change Mode"))) + return KErrPermissionDenied; + } + return KErrNone; + } + + +TInt TFsFileRename::DoRequestL(CFsRequest* aRequest) +// +// Rename the file if it was openned EFileShareExclusive +// + { + __PRINT(_L("TFsFileRename::DoRequestL(CFsRequest* aRequest)")); + + TInt r = CheckDiskSpace(0, aRequest); + if(r != KErrNone) + return r; + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + TInt currentMode=(share->iMode&KFileShareMask); + if ((currentMode&EFileShareAny) || (currentMode&EFileShareReadersOnly)) + return(KErrAccessDenied); // File must be EFileShareExclusive + + if ((share->iMode&EFileWrite)==0) + return(KErrAccessDenied); // File must have write permission + + TPtrC filePath = aRequest->Dest().FullName().Mid(2); + CFileCB& file = share->File(); + + TRACEMULT2(UTF::EBorder, UTraceModuleFileSys::ECFileCBRenameL, EF32TraceUidFileSys, + (TUint) &file, filePath); + TRAP(r,file.RenameL(filePath)); + TRACERETMULT1(UTF::EBorder, UTraceModuleFileSys::ECFileCBRenameLRet, EF32TraceUidFileSys, r); + + // Re-write the file's folded name & re-calculate the hash + if (r == KErrNone) + { + TFileName filePathF; + filePathF.CopyF(filePath); + TRAP(r, AllocBufferL(file.iFileNameF, filePathF)); + file.iNameHash=CalcNameHash(*file.iFileNameF); + } + + return(r); + } + +TInt TFsFileRename::Initialise(CFsRequest* aRequest) +// +// +// + { + TInt r=DoInitialise(aRequest); + if(r!=KErrNone) + return(r); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TFileName newName; + TRAP(r,aRequest->ReadL(KMsgPtr0,newName)); + if(r!=KErrNone) + return(r); + TDriveUnit currentDrive(share->File().Mount().Drive().DriveNumber()); + TDriveName driveName=currentDrive.Name(); + r=aRequest->Dest().Set(newName,&share->File().FileName(),&driveName); + if (r!=KErrNone) + return(r); + if (aRequest->Dest().IsWild()) + return(KErrBadName); + TInt driveNo; + if ((r=RFs::CharToDrive(aRequest->Dest().Drive()[0],driveNo))!=KErrNone) + return(r); + TDrive& drive=TheDrives[driveNo]; + if(drive.IsSubsted()) + { + if ((drive.Subst().Length()+aRequest->Dest().FullName().Length())>(KMaxFileName+3)) + return(KErrBadName); + TFileName n=drive.Subst(); + n+=aRequest->Dest().FullName().Mid(3); + r=aRequest->Dest().Set(n,NULL,NULL); + if(r!=KErrNone) + return(r); + } + + TDriveUnit newDrive(aRequest->Dest().Drive()); + if (newDrive!=currentDrive) + return(KErrBadName); + if (IsIllegalFullName(aRequest->Dest().FullName().Mid(2))) + return(KErrBadName); + r=PathCheck(aRequest,aRequest->Dest().FullName().Mid(2),&KCapFsSysFileRename,&KCapFsPriFileRename,&KCapFsROFileRename, __PLATSEC_DIAGNOSTIC_STRING("File Rename")); + return(r); + } + + +TInt TFsFileDrive::DoRequestL(CFsRequest* aRequest) +// +// Get the drive info for the file. +// + { + __PRINT(_L("TFsFileDrive::DoRequestL(CFsRequest* aRequest)")); + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TDrive *dr = &share->File().Drive(); + + TPckgBuf pkiF(dr->DriveNumber()); // copy drive number to user + aRequest->WriteL(KMsgPtr0, pkiF); + + TDriveInfo di; // copy drive info to user + dr->DriveInfo(di); + TPckgC pkdiF(di); + aRequest->WriteL(KMsgPtr1, pkdiF); + + return(KErrNone); + } + + +TInt TFsFileDrive::Initialise(CFsRequest* aRequest) +// +// +// + { + return(DoInitNoParse(aRequest)); + } + + +TInt TFsFileDuplicate::DoRequestL(CFsRequest* aRequest) +// +// Duplicate the received file handle ready for transfer to another process. +// The new file handle is written back to the client in a a mangled form to prevent +// it from being used. Calling TFsFileAdopt will de-mangle the handle. +// + { + CFileShare* pS = (CFileShare*)aRequest->ScratchValue(); + + // get the file control block from the client's file share + CFileCB& fileCB = pS->File(); + + // Create a new file share and initialize it with the + // client file share's file control block, position & mode + + CFileShare* pNewFileShare = new CFileShare(&fileCB); + if (pNewFileShare == NULL) + return KErrNoMemory; + + // We need to call CFileCB::PromoteShare immediately after the CFileShare + // instance is created since the destructor calls CFileCB::DemoteShare() + // which checks the share count is non-zero + pNewFileShare->iMode = pS->iMode; + fileCB.PromoteShare(pNewFileShare); + + + TInt r = fileCB.Open(); // increment the ref count + + if (r == KErrNone) + TRAP(r, pNewFileShare->InitL()); + __e32_atomic_store_ord64(&pNewFileShare->iPos, pS->iPos); + + // Add new file share to the global file share container + if (r == KErrNone) + TRAP(r, FileShares->AddL(pNewFileShare,ETrue)); + + // Add new file share to list owned by this session + TInt newHandle; + if (r == KErrNone) + TRAP(r,newHandle = aRequest->Session()->Handles().AddL(pNewFileShare,ETrue)); + + if (r!=KErrNone) + { + pNewFileShare->Close(); + return r; + } + + newHandle^= KSubSessionMangleBit; + + TPtrC8 pH((TUint8*)&newHandle, sizeof(TInt)); + aRequest->WriteL(KMsgPtr3, pH); + aRequest->Session()->IncResourceCount(); + return(KErrNone); + } + +TInt TFsFileDuplicate::Initialise(CFsRequest* aRequest) + { + TInt handle = aRequest->Message().Int0(); + CFileShare* share = GetShareFromHandle(aRequest->Session(), handle); + + // If the handle is invalid, either panic (CFsMessageRequest::Dispatch() will + // panic if we return KErrBadHandle) or complete the request with KErrArgument. + // The latter case is the behaviour for the (deprecated) RFile::Adopt() to + // prevent a server from panicing if the passed file handle is invalid. + if(!share) + return aRequest->Message().Int1()?KErrBadHandle:KErrArgument; + + aRequest->SetDrive(&share->File().Drive()); + aRequest->SetScratchValue64( MAKE_TINT64(ETrue, (TUint) share) ); + return(KErrNone); + } + + + +TInt TFsFileAdopt::DoRequestL(CFsRequest* aRequest) +// +// Adopt the passed file handle. This assumes that the handle has already been +// duplicated by calling TFsFileDuplicate and is therefore mangled. +// + { + if (((CFileShare*)aRequest->ScratchValue()) == NULL) + return KErrBadHandle; + + TInt adoptType = aRequest->Message().Int1(); + // EFileBigFile mode check + switch(adoptType) + { + case KFileAdopt32: + // Request is from RFile::Adopt or RFile::AdoptFromXXX: Force NO EFileBigFile + ((CFileShare*)aRequest->ScratchValue())->iMode &= ~EFileBigFile; + break; + case KFileAdopt64: + // Request is from RFile64::AdoptFromXXX: Force EFileBigFile + ((CFileShare*)aRequest->ScratchValue())->iMode |= EFileBigFile; + break; + case KFileDuplicate: + // Request is from RFile::Duplucate + // adopt original file mode - do nothing + break; + //default: + // Do nothing + } + + // De-mangle the existing sub-session handle and return it to the client + TInt newHandle = aRequest->Message().Int0() ^ KSubSessionMangleBit; + + TPtrC8 pH((TUint8*)&newHandle,sizeof(TInt)); + aRequest->WriteL(KMsgPtr3,pH); + return(KErrNone); + } + +TInt TFsFileAdopt::Initialise(CFsRequest* aRequest) + { + TInt handle = aRequest->Message().Int0() ^KSubSessionMangleBit; + + CFileShare* share = GetShareFromHandle(aRequest->Session(), handle); + // Normally returning KErrBadHandle will result in a panic, but when a server calls + // RFile::AdoptXXX() and it's client has given it a bad handle then it's not a good + // idea to panic the server. So we return KErrNone here and allow + // TFsFileAdopt::DoRequestL() to return KErrBadHandle + if (share) + aRequest->SetDrive(&share->File().Drive()); + + aRequest->SetScratchValue64( MAKE_TINT64(ETrue, (TUint) share) ); + return(KErrNone); + } + + +TInt TFsFileName::DoRequestL(CFsRequest* aRequest) +// +// Get the name of a file. +// i.e. including the name & extension but excluding the drive and path +// + { + CFileShare* share = (CFileShare*)aRequest->ScratchValue(); + + // Search backwards until a backslash is found + // This should always succeed as this is a full pathname + TPtrC name(share->File().FileName()); + TInt offset = name.LocateReverse('\\'); + aRequest->WriteL(KMsgPtr0, name.Mid(offset+1)); + + return(KErrNone); + } + +TInt TFsFileName::Initialise(CFsRequest* aRequest) +// +// Get the full name of a file, including path and drive +// +// + { + return InitialiseScratchToShare(aRequest); + } + + +TInt TFsFileFullName::DoRequestL(CFsRequest* aRequest) +// +// Get the full name of a file, including path and drive +// + { + CFileShare* share = (CFileShare*)aRequest->ScratchValue(); + + // Write the drive letter and ':' + TBuf<2> driveBuf(KDrivePath); + driveBuf[0]=TText('A' + share->File().Drive().DriveNumber()); + aRequest->WriteL(KMsgPtr0, driveBuf); + + // Write the file and path including leading '\' + TPtrC name(share->File().FileName()); + aRequest->WriteL(KMsgPtr0, name, 2); + + return(KErrNone); + } + + +TInt TFsFileFullName::Initialise(CFsRequest* aRequest) +// +// +// + { + return InitialiseScratchToShare(aRequest); + } + +TInt TFsGetMediaSerialNumber::DoRequestL(CFsRequest* aRequest) +// +// Acquire capability from media and return serial number if supported. +// + { + // Get request parameters + const TInt drvNum = aRequest->Message().Int1(); + + // Get media capability + TLocalDriveCapsV5Buf capsBuf; + + TInt r = KErrNone; + + // is the drive local? + if (!IsProxyDrive(drvNum)) + { + // if not valid local drive, use default values in localDriveCaps + // if valid local drive and not locked, use TBusLocalDrive::Caps() values + // if valid drive and locked, hard-code attributes + r = GetLocalDrive(drvNum).Caps(capsBuf); + } + else // this need to be made a bit nicer + { + CExtProxyDrive* pD = GetProxyDrive(drvNum); + if(pD) + r = pD->Caps(capsBuf); + else + r = KErrNotReady; + } + + if (r != KErrNone) + return r; + + TLocalDriveCapsV5& capsV5 = capsBuf(); + + // Return serial number if supported + if (capsV5.iSerialNumLength == 0) + return KErrNotSupported; + + TPtrC8 snPtr(capsV5.iSerialNum, capsV5.iSerialNumLength); + aRequest->WriteL(KMsgPtr0, snPtr); + + return KErrNone; + } + +TInt TFsGetMediaSerialNumber::Initialise(CFsRequest* aRequest) +// +// Validate drive number and its attributes passed in a request object. +// + { + const TInt drvNum = aRequest->Message().Int1(); + + TInt nRes = ValidateDrive(drvNum, aRequest); + if(nRes != KErrNone) + return KErrBadName; //-- incorrect drive number + + if(aRequest->Drive()->IsSubsted()) + return KErrNotSupported; //-- the drive is substed, this operation doesn't make a sense + + if(!IsValidLocalDriveMapping(drvNum)) + return KErrNotReady; + + return KErrNone; + } + +TInt TFsBlockMap::Initialise(CFsRequest* aRequest) + { + TInt r = DoInitNoParse(aRequest); + if(r!=KErrNone) + return r; + + TInt blockMapUsage = aRequest->Message().Int2(); + if ( blockMapUsage == EBlockMapUsagePaging ) + { + CFileShare* share = (CFileShare*) aRequest->ScratchValue(); + CFileCB& file = share->File(); + + // To determine whether the drive where this file resides is pageable, we need to locate + // the specific TBusLocalDrive object first; querying the drive attributes directly from the + // (composite) file system is no good as this API treats all slave file systems as "ROM & not pageable. + TBusLocalDrive* localDrive; + TInt r = file.LocalDrive(localDrive); + if (r != KErrNone) + return r; + TLocalDriveCapsV4Buf caps; + r = localDrive->Caps(caps); + if (r != KErrNone) + return r; + __PRINT4(_L("TFsBlockMap::Initialise, drive %d file %S iMediaAtt %08X iDriveAtt %08X\n"), file.DriveNumber(), &file.FileName(), caps().iMediaAtt, caps().iDriveAtt); + if ( !(caps().iDriveAtt & KDriveAttPageable)) + return KErrNotSupported; + } + + return KErrNone; + } + +TInt TFsBlockMap::DoRequestL(CFsRequest* aRequest) + { + __PRINT(_L("TFsBlockMap::DoRequestL(CFsRequest* aRequest)")); + __PRINT1(_L("aRequest->Session() = 0x%x"), aRequest->Session()); + + CFileShare* share = (CFileShare*) aRequest->ScratchValue(); + + TInt r = share->CheckMount(); + + __PRINT1(_L("share->CheckMount() returned - %d"), r); + if ( r != KErrNone ) + return(r); + + SBlockMapInfo reqInfo; + SBlockMapArgs args; + TPckg pkArgs(args); + aRequest->ReadL(KMsgPtr1, pkArgs); + + CFileCB& file = share->File(); + TInt64& reqStartPos = args.iStartPos; + TInt64 reqEndPos = args.iEndPos; + + if ( ( reqStartPos > file.Size64() ) || ( reqStartPos < 0 ) ) + return KErrArgument; + + const TInt64 KReadToEOF = -1; + if ( reqEndPos != KReadToEOF ) + { + if ( !( reqEndPos >= reqStartPos ) ) + return KErrArgument; + } + else + reqEndPos = file.Size64(); + + // If the requested start position is equal to the size of the file + // then we read no data and return an empty BlockMap. + if ( reqStartPos == file.Size64() || reqEndPos == 0 || reqEndPos == reqStartPos ) + { + TPckg pkInfo(reqInfo); + TRAP(r,aRequest->WriteL(KMsgPtr0,pkInfo) ); + if ( r == KErrNone ) + return(KErrArgument); + else + return(r); + } + r = share->File().BlockMap(reqInfo, reqStartPos, reqEndPos); + TPckg pkInfo(reqInfo); + aRequest->WriteL(KMsgPtr0, pkInfo); + aRequest->WriteL(KMsgPtr1, pkArgs); + + return(r); + } + +#pragma warning( disable : 4705 ) // statement has no effect +/** +Default constructor +*/ +EXPORT_C CFileCB::CFileCB() + { + } +#pragma warning( default : 4705 ) + + + + +/** +Destructor. + +Frees resources before destruction of the object. +*/ +EXPORT_C CFileCB::~CFileCB() + { + // NB Must be careful to close the file cache BEFORE deleting iFileNameF + // as CFileCache may steal it (!) + if (FileCache()) + FileCache()->Close(); + if (iBody && iBody->iDeleteOnClose) + { + TRACEMULT2(UTF::EBorder, UTraceModuleFileSys::ECMountCBDeleteL, EF32TraceUidFileSys, DriveNumber(), FileName()); + TInt r; + TRAP(r, iMount->DeleteL(FileName())); + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECMountCBDeleteLRet, EF32TraceUidFileSys, r); + } + + if(iMount) + iMount->Close(); + if (iMountLink.iNext!=NULL) + { + iMountLink.Deque(); + } + delete iFileName; + delete iFileNameF; + if (iFileLocks) + { + iFileLocks->Close(); + delete iFileLocks; + } + + delete iBody; + } + +/** + Initialise CFileCB object. + @internalTechnology + + @param aDrive + @param aCreatedDrive + @param aName file name descriptor +*/ +EXPORT_C void CFileCB::InitL(TDrive* aDrive, TDrive* aCreatedDrive, HBufC* aName) + { + // Take ownership of heap-allocated objects aName and aLock before attempting any memory allocation + // to avoid leaking memory + iFileName = aName; + + DoInitL(aDrive->DriveNumber()); + iDrive=aDrive; + iCreatedDrive=aCreatedDrive; + TFileName tempName; + tempName.CopyF(*aName); + iFileNameF=tempName.AllocL(); + iNameHash=CalcNameHash(*iFileNameF); + + + + // see whether the file system supports the CFileCB extended API + MExtendedFileInterface* extendedFileInterface = NULL; + GetInterfaceTraced(CFileCB::EExtendedFileInterface, (void*&) extendedFileInterface, NULL); + iBody = new(ELeave)CFileBody(this, extendedFileInterface); + + iMount=&iDrive->CurrentMount(); + iBody->InitL(); + User::LeaveIfError(iMount->Open()); + + //-- create file locks array + ASSERT(!iFileLocks); + iFileLocks = new(ELeave) TFileLocksArray(KFileShareLockGranularity, _FOFF(TFileShareLock, iPosLow)); + + } + + +TInt CFileCB::FindLock(TInt aPosLow,TInt aPosHigh) + { + return FindLock64(aPosLow, aPosHigh); + } + +TInt CFileCB::AddLock(CFileShare* aFileShare,TInt aPos,TInt aLength) + { + return AddLock64(aFileShare, aPos, aLength); + } + +TInt CFileCB::RemoveLock(CFileShare* aFileShare,TInt aPos,TInt aLength) + { + return RemoveLock64(aFileShare, aPos, aLength); + } + +TInt CFileCB::CheckLock(CFileShare* aFileShare,TInt aPos,TInt aLength) + { + return CheckLock64(aFileShare, aPos, aLength); + } + +/** + Remove any locks held by aFileShare. +*/ +void CFileCB::RemoveLocks(CFileShare* aFileShare) + { + + TInt i=0; + while (iiMode & KFileShareMask); + if(reqShare != EFileShareReadersOrWriters) + { + iBody->iPromotedShares++; + iShare = reqShare; + } + } + + +void CFileCB::DemoteShare(CFileShare* aShare) +// +// Manages share demotion after the share has been removed from the FileShares container. +// +// - If the share being removed is not EFileShareReadersOrWriters, then the current +// share mode may require demotion back to EFileShareReadersOrWriters. +// +// - This is determined by the iPromotedShares count, incremented in PromoteShare() +// + { + if((aShare->iMode & KFileShareMask) != EFileShareReadersOrWriters) + { + if(--iBody->iPromotedShares == 0) + { + // Don't worry if the file has never been opened as EFileShareReadersOrWriters + // - in this case the CFileCB object is about to be closed anyway. + iShare = EFileShareReadersOrWriters; + } + } + __ASSERT_DEBUG(iBody->iPromotedShares>=0,Fault(EFileShareBadPromoteCount)); + } + + +RArray& CFileCB::AsyncReadRequests() +// +// Gets a reference to the pending asynchronous read requests for this file. +// +// - The request is completed when all data is available or the request is cancelled. +// + { + return(*iBody->iAsyncReadRequests); + } + + +TInt CFileCB::AddAsyncReadRequest(CFileShare* aShareP, TInt64 aPos, TInt aLen, CFsRequest* aRequestP) +// +// Adds a pending asynchronous read request to the list. +// +// - The request is completed when all data is available or the request is cancelled. +// + { + + __ASSERT_ALWAYS(aRequestP->Operation()->Function() == EFsFileRead, Fault(EBaseRequestMessage)); + + TAsyncReadRequest req(aPos + aLen, aShareP, aRequestP); + TInt err = AsyncReadRequests().InsertInSignedKeyOrderAllowRepeats(req); + if(err != KErrNone) + return err; + + aRequestP->SetCompleted(EFalse); + return KErrNone; + } + + +TInt CFileCB::CancelAsyncReadRequest(CFileShare* aShareP, TRequestStatus* aStatusP) +// +// Cancels (and completes) an outstanding read request for the specified share. +// +// - aStatusP == NULL cancels all outstanding read requests. +// + { + + TInt i=0; + Drive().Lock(); + while (i < AsyncReadRequests().Count()) + { + TAsyncReadRequest& req = AsyncReadRequests()[i]; + if(req.CompleteIfMatching(aShareP, aStatusP, KErrCancel)) + { + iBody->iAsyncReadRequests->Remove(i); + if(aStatusP != NULL) + { + Drive().UnLock(); + return KErrNone; + } + } + else + { + i++; + } + } + + Drive().UnLock(); + return KErrNone; + } + + +void CFileCB::NotifyAsyncReaders() +// +// Determine if any outstanding read requests require completion. +// +// - Called whenever the file size changes (due to a write operation or SetSize) +// +// - Any outstanding read requests are re-issued (rather then completed in the +// context of the current operation so not to affect performance of the writer). +// +// - Should the file size shrink before the request is serviced, the request will +// be added back onto the queue. +// +// - A future optimisation may issue the request as data becomes available (which +// would minimise the final latency between writer and reader) but the current +// implementation reads all requested data in one operation. +// + { + Drive().Lock(); + + SetNotifyAsyncReadersPending(EFalse); + + while(AsyncReadRequests().Count()) + { + TAsyncReadRequest& req = AsyncReadRequests()[0]; + if(req.iEndPos > CachedSize64()) + break; + + // Make a copy and then remove it from the queue before releasing the lock - + // because the file server thread could append to the RArray and this might + // cause a re-allocation which would move the whole array (!) + TAsyncReadRequest reqCopy = req; + AsyncReadRequests().Remove(0); + Drive().UnLock(); + + // allocate a new request, push a TMsgOperation onto the request's stack (duplicating + // the functionality of TFsFileRead::Initialise()) & dispatch the request + CFsClientMessageRequest* pRequest = NULL; + const TOperation& oP = OperationArray[EFsFileRead]; + TInt r = RequestAllocator::GetMessageRequest(oP, reqCopy.iMessage, pRequest); + if (r != KErrNone) + { + reqCopy.iMessage.Complete(r); // complete the client's message with an error + continue; + } + pRequest->Set(reqCopy.iMessage, oP, reqCopy.iSessionP); + + r = DoInitNoParse(pRequest); + if (r != KErrNone) + { + pRequest->Complete(r); // complete the client's message with an error + continue; + } + + r = pRequest->PushOperation(TFsFileRead::Complete); + if (r != KErrNone) + { + pRequest->Complete(r); // complete the client's message with an error + continue; + } + + pRequest->CurrentOperation().Set( + reqCopy.iEndPos - pRequest->Message().Int1(), + pRequest->Message().Int1(), + (TDes8*) pRequest->Message().Ptr0()); + + // don't call Initialise() + pRequest->SetState(CFsRequest::EReqStatePostInitialise); + + pRequest->Dispatch(); + Drive().Lock(); + } + Drive().UnLock(); + } + + +/** +Gets the address of the file that the file control block represents. + +The default implementation returns KErrNotSupported and should only +be overridden for ROM file systems. + +@param aPos On return, should contain the address of the file that + the file control block represents. + +@return KErrNone, if successful,otherwise one of the other system wide error + codes, +*/ +EXPORT_C TInt CFileCB::Address(TInt& /*aPos*/) const + { + + return(KErrNotSupported); + } + + +/** +Sets the archive attribute, KEntryAttArchive, in iAtt. +*/ +EXPORT_C void CFileCB::SetArchiveAttribute() + { + + iAtt|=KEntryAttArchive; + iAtt|=KEntryAttModified; + iModified.UniversalTime(); + } + + +EXPORT_C TInt CFileCB::GetInterface(TInt aInterfaceId,TAny*& aInterface,TAny* /*aInput*/) + { + switch(aInterfaceId) + { + case EGetLocalDrive: + return Mount().LocalDrive((TBusLocalDrive*&) aInterface); + default: + return(KErrNotSupported); + } + } + + +CFileCache* CFileCB::FileCache() const + {return iBody?iBody->iFileCache:NULL;} + +TBool CFileCB::LocalBufferSupport() const + {return iBody?iBody->iLocalBufferSupport:EFalse;} + +void CFileCB::SetLocalBufferSupport(TBool aEnabled) + {iBody->iLocalBufferSupport = aEnabled;} + +TInt CFileCB::CheckMount() +// +// Check that the media is still mounted. +// + { + + TDrive& d = Drive(); + TInt r=d.CheckMount(); + if (r!=KErrNone) + return(r); + if (&Mount() != &d.CurrentMount()) + return(KErrDisMounted); + if (FileCorrupt()) + return(KErrCorrupt); + if (BadPower()) + { + if (PowerOk()) + SetBadPower(EFalse); + else + return(KErrBadPower); + } + return(KErrNone); + } + +TInt64 CFileCB::CachedSize64() const + { + CFileCache* fileCache = iBody?iBody->iFileCache:NULL; + return fileCache? fileCache->Size64(): Size64(); + } + +void CFileCB::SetCachedSize64(TInt64 aSize) + { + if (FileCache()) + FileCache()->SetSize64(aSize); + else + SetSize64(aSize, EFalse); // assume not locked + } + +/** +Constructor. +Locks the mount resource to which the shared file resides. + +@param aFileCB File to be shared. +*/ +CFileShare::CFileShare(CFileCB* aFileCB) + : iFile(aFileCB) + { + AddResource(iFile->Mount()); + } + +/** +Destructor. + +Frees mount resource to which the shared file resides, +removes share status from the shared file and finally closes +the file. +*/ +CFileShare::~CFileShare() + { + // We shouldn't be deleting the file share with a valid request queue or there will be request (& memory) leakage + __ASSERT_DEBUG(iCurrentRequest == NULL, Fault(ERequestQueueNotEmpty)); + + RemoveResource(iFile->Mount()); + iFile->RemoveLocks(this); + iFile->DemoteShare(this); + iFile->CancelAsyncReadRequest(this, NULL); + iFile->Close(); + } + +/** +Check that the media is still mounted. + +@return KErrNone if successful. + KErrDisMounted if media has dismounted. + KErrCorrupted if shared file is corrupted. + KErrBadPower if insufficent power supply. + or other system wide error code. +*/ +TInt CFileShare::CheckMount() + { + return File().CheckMount(); + } + +/** +Initialise the object +*/ +void CFileShare::InitL() + { + DoInitL(iFile->Drive().DriveNumber()); + + // override the close operation so that we can flush the write cache if necessary + iRequest->Set(FileShareCloseOp,NULL); + iRequest->SetDriveNumber(DriveNumber()); + iRequest->SetScratchValue((TUint)this); + } + +// Mark the start of a request - +// the is to prevent fair-scheduled async read/writes from being processed out of sequence. This is especially +// important when considering a client which appends to a file by issuing more than one asynchronous request as each +// write request must be entirely satisfied before a subsequent request can append to the file +TBool CFileShare::RequestStart(CFsMessageRequest* aRequest) + { + TBool ret; + + TDrive& drive = File().Drive(); + drive.Lock(); + + if (iCurrentRequest == NULL || iCurrentRequest == aRequest) + { + iCurrentRequest = aRequest; + ret = ETrue; + } + else + { + // add to end of linked list of requests if there is already an active request for this share + CFsClientMessageRequest* request; + for (request = (CFsClientMessageRequest*) iCurrentRequest; request->iNext != NULL; request = request->iNext) + { + } + request->iNext = (CFsClientMessageRequest*) aRequest; + ret = EFalse; + } + + drive.UnLock(); + return ret; + } + + +// Mark the end of a request +void CFileShare::RequestEnd(CFsMessageRequest* aRequest) + { + TDrive& drive = File().Drive(); + drive.Lock(); + + if (aRequest == iCurrentRequest) + { + // Any requests in the queue ? + if (((CFsClientMessageRequest*) iCurrentRequest)->iNext) + { + iCurrentRequest = ((CFsClientMessageRequest*) aRequest)->iNext; + ((CFsClientMessageRequest*) aRequest)->iNext = NULL; + + // if the current request has been cancelled, cancel all requests in the queue + TInt lastError = aRequest->LastError(); + if (lastError == KErrCancel || lastError == KErrNotReady) + { + // take ownership of this queue and make it invisible to anyone else by setting iCurrentRequest to NULL + CFsClientMessageRequest* currentRequest = (CFsClientMessageRequest*) iCurrentRequest; + iCurrentRequest = NULL; + drive.UnLock(); + while(currentRequest) + { + CFsClientMessageRequest* nextRequest = ((CFsClientMessageRequest*) currentRequest)->iNext; + ((CFsClientMessageRequest*) currentRequest)->iNext = NULL; + currentRequest->Complete(lastError); + currentRequest = nextRequest; + } + } + else + { + drive.UnLock(); + iCurrentRequest->Dispatch(EFalse); + } + } + else // queue empty + { + iCurrentRequest = NULL; + drive.UnLock(); + } + } + else // if (aRequest == iCurrentRequest) + { + drive.UnLock(); + } + } + +TBool CFileShare::RequestInProgress() const + { + return (iCurrentRequest != NULL)?(TBool)ETrue:(TBool)EFalse; + } + + + +/** +Initialise the object +*/ +TInt TFsCloseFileShare::DoRequestL(CFsRequest* aRequest) +// + { + __PRINT(_L("TFsCloseFileCache::DoRequestL()")); + + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + + // flush the write cache before closing the file share + TInt r; + CFileCache* fileCache = share->File().FileCache(); + if (fileCache && (r = fileCache->FlushDirty(aRequest)) == CFsRequest::EReqActionBusy) + return r; + + return KErrNone; + } + +TInt TFsCloseFileShare::Complete(CFsRequest* aRequest) + { + __PRINT(_L("TFsCloseFileShare::Complete()")); + return TFsCloseObject::Complete(aRequest); + } + + +TAsyncReadRequest::TAsyncReadRequest(TInt64 aEndPos, CFileShare* aOwningShareP, CFsRequest* aRequestP) +// +// Constructor for TAsyncReadRequest +// - Maintains information about oustanding async read requests +// + : iEndPos(aEndPos), + iOwningShareP(aOwningShareP) + { + iMessage = aRequestP->Message(); + iSessionP = aRequestP->Session(); + iStatusP = iMessage.ClientStatus(); + } + + +TBool TAsyncReadRequest::CompleteIfMatching(CFileShare* aShareP, TRequestStatus* aStatusP, TInt aError) +// +// Completes an asynchronous read request. +// + { + if (iOwningShareP == aShareP && (aStatusP == NULL || aStatusP == iStatusP)) + { + iMessage.Complete(aError); + return ETrue; + } + + return EFalse; + } + + +TInt TFsFileReadCancel::Initialise(CFsRequest* aRequest) +// +// Initialise function for RFile::ReadCancel [EFsReadCancel] +// + { + return InitialiseScratchToShare(aRequest); + } + + +TInt TFsFileReadCancel::DoRequestL(CFsRequest* aRequest) +// +// Request function for RFile::ReadCancel [EFsReadCancel] +// + { + CFileShare* share = (CFileShare*)aRequest->ScratchValue(); + TRequestStatus* status = (TRequestStatus*)aRequest->Message().Ptr0(); + share->File().CancelAsyncReadRequest(share, status); + return(KErrNone); + } + +void CFileCB::ReadL(TInt64 aPos,TInt& aLength,TDes8* aDes,const RMessagePtr2& aMessage, TInt aOffset) + { + TRACETHREADID(aMessage); + TRACE7(UTF::EBorder, UTraceModuleFileSys::ECFileCBReadL, EF32TraceUidFileSys, + this, I64LOW(aPos), I64HIGH(aPos), aLength, aDes, threadId, aOffset); + + iBody->iExtendedFileInterface->ReadL(aPos,aLength,aDes,aMessage,aOffset); + + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECFileCBReadLRet, EF32TraceUidFileSys, KErrNone); + } + +void CFileCB::WriteL(TInt64 aPos,TInt& aLength,const TDesC8* aDes,const RMessagePtr2& aMessage, TInt aOffset) + { + TRACETHREADID(aMessage); + TRACE7(UTF::EBorder, UTraceModuleFileSys::ECFileCBWriteL, EF32TraceUidFileSys, + this, I64LOW(aPos), I64HIGH(aPos), aLength, aDes, threadId, aOffset); + + iBody->iExtendedFileInterface->WriteL(aPos,aLength,aDes,aMessage,aOffset); + + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECFileCBWriteLRet, EF32TraceUidFileSys, KErrNone); + } + +void CFileCB::SetSizeL(TInt64 aSize) + { + TRACE3(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetSizeL, EF32TraceUidFileSys, this, I64LOW(aSize), I64HIGH(aSize)); + + iBody->iExtendedFileInterface->SetSizeL(aSize); + + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECFileCBSetSizeLRet, EF32TraceUidFileSys, KErrNone); + } + +TBool CFileCB::ExtendedFileInterfaceSupported() + { + return iBody->ExtendedFileInterfaceSupported(); + } + +TInt CFileCB::FairSchedulingLen() const + { + return iBody->iFairSchedulingLen; + } + +void CFileCB::SetNotifyAsyncReadersPending(TBool aNotifyAsyncReadersPending) +// +// Notify the asynchronous reader that a file has grown so that it may service outstanding async reads +// + { + iBody->iNotifyAsyncReadersPending = aNotifyAsyncReadersPending; + } + +TBool CFileCB::NotifyAsyncReadersPending() const + { + return iBody->iNotifyAsyncReadersPending; + } + + +void CFileCB::ResetReadAhead() + { + CFileCache* fileCache = FileCache(); + if (fileCache) + fileCache->ResetReadAhead(); + } + +void CFileCB::SetDeleteOnClose() + { + iBody->iDeleteOnClose = ETrue; + } + +TBool CFileCB::DeleteOnClose() const + { + return iBody->iDeleteOnClose; + } + +TInt CFileCB::GetInterfaceTraced(TInt aInterfaceId, TAny*& aInterface, TAny* aInput) + { + TRACE2(UTF::EBorder, UTraceModuleFileSys::ECFileCBGetInterface, EF32TraceUidFileSys, aInterfaceId, aInput); + + TInt r = GetInterface(aInterfaceId, aInterface, aInput); + + TRACERET2(UTF::EBorder, UTraceModuleFileSys::ECFileCBGetInterfaceRet, EF32TraceUidFileSys, r, aInterface); + + return r; + } + +CFileBody::CFileBody(CFileCB* aFileCB, CFileCB::MExtendedFileInterface* aExtendedFileInterface) + : iFileCB(aFileCB), + iExtendedFileInterface(aExtendedFileInterface ? aExtendedFileInterface : this), + iSizeHigh(0) + { + iFairSchedulingLen = TFileCacheSettings::FairSchedulingLen(iFileCB->DriveNumber()); + iMaxSupportedFileSize = KMaxSupportedFileSize; + } + + +CFileBody::~CFileBody() + { + if (iAsyncReadRequests) + { + iAsyncReadRequests->Close(); + delete iAsyncReadRequests; + } + } + + +void CFileBody::InitL() + { + iAsyncReadRequests = new(ELeave) RArray(KAsyncRequestArrayGranularity, _FOFF(TAsyncReadRequest, iEndPos)); + } + + + +TInt TFsFileClamp::Initialise(CFsRequest* aRequest) +// +// Initialise function for RFile::Clamp [EFsFileClamp] +// + { + TSecureId aUID = aRequest->Message().SecureId(); + if (aUID!=KEstartUidValue && aUID!=KFileServerUidValue) + { + SSecurityInfo info; + info.iVendorId=0; + info.iCaps.iCaps[0]=0; + info.iCaps.iCaps[1]=0; + info.iSecureId=KEstartUidValue; + PlatSec::PolicyCheckFail(aRequest->Message(),info,"File Clamp"); + info.iSecureId=KFileServerUidValue; + PlatSec::PolicyCheckFail(aRequest->Message(),info,"File Clamp"); + return KErrPermissionDenied; + } + + TInt r=DoInitialise(aRequest); + if(r!=KErrNone) + return r; + + // The clamp API is only supported on non-removable media + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + TDriveInfo di; + share->File().Drive().DriveInfo(di); + if (!(di.iDriveAtt & KDriveAttInternal)) + r = KErrNotSupported; + + return(r); + } + + +TInt TFsFileClamp::DoRequestL(CFsRequest* aRequest) +// +// Request function for RFile::Clamp [EFsFileClamp] +// + { + TInt r; + + // Flush data for this file, if it is open for writing, before clamping + CFileShare* share=(CFileShare*)aRequest->ScratchValue(); + + if (((share->iMode&EFileWrite)) || ((share->File().Att()&KEntryAttModified))) + { + r=share->CheckMount(); + if (r!=KErrNone) + return(r); + + TRACE1(UTF::EBorder, UTraceModuleFileSys::ECFileCBFlushDataL, EF32TraceUidFileSys, &share->File()); + TRAP(r,share->File().FlushDataL()); + TRACERET1(UTF::EBorder, UTraceModuleFileSys::ECFileCBFlushDataLRet, EF32TraceUidFileSys, r); + + if(r!=KErrNone) + return(r); + } + + RFileClamp clamp; + r=aRequest->Drive()->ClampFile(aRequest->Src().FullName().Mid(2), + (TAny*)(&clamp)); + // Write clamp information to user + TPckgC pkClamp(clamp); + aRequest->WriteL(KMsgPtr0, pkClamp); + return r; + } + + +TInt TFsUnclamp::Initialise(CFsRequest* aRequest) +// +// Initialise function for RFs::Unclamp [EFsUnclamp] +// + { + TSecureId aUID = aRequest->Message().SecureId(); + if (aUID!=KEstartUidValue && aUID!=KFileServerUidValue) + { + SSecurityInfo info; + info.iVendorId=0; + info.iCaps.iCaps[0]=0; + info.iCaps.iCaps[1]=0; + info.iSecureId=KEstartUidValue; + PlatSec::PolicyCheckFail(aRequest->Message(),info,"File Unclamp"); + info.iSecureId=KFileServerUidValue; + PlatSec::PolicyCheckFail(aRequest->Message(),info,"File Unclamp"); + return KErrPermissionDenied; + } + RFileClamp clamp; + TPckg pkClamp(clamp); + aRequest->ReadL(KMsgPtr0, pkClamp); + TInt driveNo=(I64HIGH(clamp.iCookie[1])); + TDrive& drive=TheDrives[driveNo]; + aRequest->SetDrive(&drive); + return KErrNone; + } + + +TInt TFsUnclamp::DoRequestL(CFsRequest* aRequest) +// +// Request function for RFs::Unclamp [EFsUnclamp] +// + { + RFileClamp clamp; + TPckg pkClamp(clamp); + aRequest->ReadL(KMsgPtr0, pkClamp); + TDrive* drive=aRequest->Drive(); + CMountCB* mount=(CMountCB*)&(drive->CurrentMount()); + return(drive->UnclampFile(mount,&clamp)); + } + +CMountBody::CMountBody(CMountCB* aMountCB, CMountCB::MFileAccessor* aFileAccessor, CMountCB::MFileExtendedInterface* aFileInterface) +// +// Constructor for private body class +// + : iMountCB(aMountCB), + iFileAccessor(aFileAccessor?aFileAccessor:this), + iFileExtendedInterface(aFileInterface?aFileInterface:this) + { + } + +CMountBody::~CMountBody() +// +// Destructor for private body class +// + { + __ASSERT_DEBUG(iClampIdentifiers.Count() == 0, User::Invariant()); + iClampIdentifiers.Close(); + } + +TInt CMountBody::ClampFile(const TInt aDriveNo,const TDesC& aName,TAny* aHandle) + { + // Need CMountCB::MFileAccessor interface support + if(iFileAccessor==this) + return KErrNotSupported; + + // Get unique identifier for the file + TInt64 uniqueId = 0; + TInt r = iFileAccessor->GetFileUniqueId(aName,uniqueId); + if(r!=KErrNone) + return r; + + // Populate the RFileClamp clamp instance and store it in iClampIdentifiers + RFileClamp* newClamp = (RFileClamp*)aHandle; + newClamp->iCookie[0]=uniqueId; + newClamp->iCookie[1]=MAKE_TINT64(aDriveNo,++iClampCount); + r = iClampIdentifiers.InsertInOrder((const RFileClamp&)*newClamp,&CompareClampsByIdAndCount); + if(r != KErrNone) + return r; + + // Indicate that (at least) one file is clamped on this drive + iMountCB->Drive().SetClampFlag(ETrue); + AddResource(*iMountCB); + return KErrNone; + } + + +TInt CMountBody::UnclampFile(RFileClamp* aHandle) + { + // Need CMountCB::MFileAccessor interface support + if(iFileAccessor==this) + return KErrNotSupported; + + TInt idx; + if((idx = iClampIdentifiers.Find((const RFileClamp&)*aHandle,&FindClampByIdAndCount)) < KErrNone) + { + // This file is not 'clamped' + return idx; + } + + // If we're removing the last clamp and a dismount has been deferred (due to files being clamped), + // then DeferredDismount() will trigger a dismount: before this happens we need to flush all + // dirty data on this drive; + TDrive& drive = iMountCB->Drive(); + TInt noOfClamps = NoOfClamps(); + if (noOfClamps == 1 && drive.DismountDeferred()) + { + TInt r = drive.FlushCachedFileInfo(ETrue); + if (r == CFsRequest::EReqActionBusy) + return r; + } + + RemoveResource(*iMountCB); + iClampIdentifiers.Remove(idx); + + TInt r = KErrNone; + // If this was the last clamp, check for outstanding dismount requests + if (noOfClamps == 1) + { + ASSERT(NoOfClamps() == 0); + drive.SetClampFlag(EFalse); + if (drive.DismountDeferred()) + r = drive.DeferredDismount(); + } + + return r; + } + + +TInt CMountBody::IsFileClamped(const TInt64 aUniqueId) + { + // Need CMountCB::MFileAccessor interface support + if(iFileAccessor==this) + return KErrNotSupported; + + // Encapsulate the unique identifier in an appropriate class + RFileClamp newClamp; + newClamp.iCookie[0]=aUniqueId; + // Search for (any) entry in iClampIdentifiers holding this value + TInt index=iClampIdentifiers.Find((const RFileClamp&)newClamp,&FindClampById); + return (index==KErrNotFound?0:1); + } + +TInt CMountBody::NoOfClamps() + { + // Need CMountCB::MFileAccessor interface support + if(iFileAccessor==this) + return KErrNotSupported; + + // This will return zero if ClampFile has not previously been invoked + return iClampIdentifiers.Count(); + } + +TInt CMountBody::CompareClampsById(const RFileClamp& aClampA, const RFileClamp& aClampB) + { + if(aClampA.iCookie[0] < aClampB.iCookie[0]) return 1; + if(aClampA.iCookie[0] > aClampB.iCookie[0]) return -1; + return 0; + } + +TInt CMountBody::CompareClampsByIdAndCount(const RFileClamp& aClampA, const RFileClamp& aClampB) + { + if(aClampA.iCookie[0] > aClampB.iCookie[0]) return 1; + if(aClampA.iCookie[0] < aClampB.iCookie[0]) return -1; + // Now compare the count values + if(I64LOW(aClampA.iCookie[1]) > I64LOW(aClampB.iCookie[1])) return 1; + if(I64LOW(aClampA.iCookie[1]) < I64LOW(aClampB.iCookie[1])) return -1; + return 0; + } + +TInt CMountBody::FindClampById(const RFileClamp& aClampA, const RFileClamp& aClampB) + { + return (TInt)(!CompareClampsById(aClampA, aClampB)); + } + + +TInt CMountBody::FindClampByIdAndCount(const RFileClamp& aClampA, const RFileClamp& aClampB) + { + return (TInt)(!CompareClampsByIdAndCount(aClampA, aClampB)); + } + +void CMountBody::SetProxyDriveDismounted() + { + iProxyDriveDismounted = ETrue; + } + +TBool CMountBody::ProxyDriveDismounted() + { + return iProxyDriveDismounted; + } + + +TInt CMountBody::GetFileUniqueId(const TDesC& /*aName*/, TInt64& /*aUniqueId*/) + { + return KErrNotSupported; + } +TInt CMountBody::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) + { + return KErrNotSupported; + } +TInt CMountBody::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) + { + return KErrNotSupported; + } +TInt CMountBody::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/) + { + return KErrNotSupported; + } +void CMountBody::ReadSection64L(const TDesC& aName,TInt64 aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage) + { + if((TUint64)aPos > KMaxLegacyFileSize) + User::Leave(KErrNotSupported); + + iMountCB->ReadSectionL(aName, I64LOW(aPos), aTrg, aLength, aMessage); + } + +TBool CFileBody::ExtendedFileInterfaceSupported() + { + return (iExtendedFileInterface==this) ? (TBool)EFalse : (TBool)ETrue; + } + +// default implementations of MExtendedFileInterface +void CFileBody::ReadL(TInt64 aPos,TInt& aLength,TDes8* aDes,const RMessagePtr2& aMessage, TInt aOffset) + { + if ((TUint64)aPos > KMaxLegacyFileSize || aOffset > 0) + User::Leave(KErrNotSupported); + + iFileCB->ReadL((TInt) aPos, aLength, aDes, aMessage); + } + +void CFileBody::WriteL(TInt64 aPos,TInt& aLength,const TDesC8* aDes,const RMessagePtr2& aMessage, TInt aOffset) + { + if ((TUint64)aPos > KMaxLegacyFileSize || aOffset > 0) + User::Leave(KErrNotSupported); + + iFileCB->WriteL((TInt) aPos, aLength, aDes, aMessage); + } + +void CFileBody::SetSizeL(TInt64 aSize) + { + if ((TUint64)aSize > KMaxLegacyFileSize) + User::Leave(KErrNotSupported); + + iFileCB->SetSizeL((TInt) aSize); + } + +//--------------------------------------------------------------------------------------------------------------------- +/** + This method allows file system to set maximum file size it supports. + This can be called on instantiation of the CFileCB derived class object by the file system implementation. + If this method is not called, the iMaxSupportedFileSize will have default value KMaxTUint64 + + @param aMaxFileSize maximum file size supported by file system +*/ +EXPORT_C void CFileCB::SetMaxSupportedSize(TUint64 aMaxFileSize) + { + iBody->iMaxSupportedFileSize = aMaxFileSize; + } + +/** + @return maximum supported file size (depends on the file system created it) +*/ +TUint64 CFileCB::MaxSupportedSize(void) const + { + return iBody->iMaxSupportedFileSize; + } + +//--------------------------------------------------------------------------------------------------------------------- + +/** +Gets the size of the file. + +This is 64-bit variant for CFileCB::Size(). +This shall be used by File Systems supporting file size > 4GB - 1 to query the file size +inplace of CFileCB::Size() or CFileCB::iSize. + +@see CFileCB::iSize +@see CFileCB::Size() + +@prototype + +@return The size of the file. +*/ +EXPORT_C TInt64 CFileCB::Size64() const + { + __ASSERT_DEBUG(iBody != NULL, Fault(EFileBodyIsNull)); + const TInt64 size = MAKE_TINT64(iBody->iSizeHigh,iSize); + return size; + } + +//--------------------------------------------------------------------------------------------------------------------- +/** +Sets the size of the file. + +This is 64-bit variant for CFileCB::SetSize(). +This should be used by File Systems supporting file size > 4GB - 1 to set the file size +inplace of CFileCB::SetSize() or CFileCB::iSize. + +@see CFileCB::iSize +@see CFileCB::SetSize() + +@prototype + +@param aSize The size of the file. +@param aDriveLocked The status of the Drive Lock. If it is EFalse, +the file size shall be modified after acquiring the iLock mutex and if it is ETrue, +the file size shall be modified without aquiring the iLock mutex. +*/ +EXPORT_C void CFileCB::SetSize64(TInt64 aSize, TBool aDriveLocked) + { + if(aDriveLocked) + { + iSize = (TInt)I64LOW(aSize); + iBody->iSizeHigh = (TInt)I64HIGH(aSize); + } + else + { + Drive().Lock(); + iSize = (TInt)I64LOW(aSize); + iBody->iSizeHigh = (TInt)I64HIGH(aSize); + Drive().UnLock(); + } + } + + +/** used to organize key comparison for the TFileShareLock*/ +TInt LockOrder(const TFileShareLock& aMatch, const TFileShareLock& anEntry) + { + + if(aMatch.PosLow() > anEntry.PosLow()) + return 1; + else if(aMatch.PosLow() < anEntry.PosLow()) + return -1; + else + return 0; + + } + +//--------------------------------------------------------------------------------------------------------------------- +/** + Find a lock inclusive of aPosLow to aPosHigh. +*/ +TInt CFileCB::FindLock64(TInt64 aPosLow, TInt64 aPosHigh) + { + + const TInt count=FileLocks().Count(); + for (TInt i=0; i (TUint64)aPosHigh) + return KErrNotFound; + + if(lock.MatchByPos(aPosLow, aPosHigh)) + return i; + } + + return KErrNotFound; + } + +//--------------------------------------------------------------------------------------------------------------------- +/** + Add a lock on a range. +*/ +TInt CFileCB::AddLock64(CFileShare* aFileShare,TInt64 aPos,TInt64 aLength) + { + const TUint64 posHigh=aPos+aLength-1; + + + {//-- Lock overflow check + const TUint64 KMaxFileSize = aFileShare->IsFileModeBig() ? MaxSupportedSize() : KMaxLegacyFileSize; + if(posHigh > KMaxFileSize) + return KErrArgument; + } + + + TInt r=CheckLock64(NULL, aPos, aLength); + if (r!=KErrNone) + return r; + + TFileShareLock lock(aFileShare, aPos, posHigh); + + TLinearOrder lockOrder(LockOrder); + r=FileLocks().InsertInOrder(lock, lockOrder); + __ASSERT_ALWAYS(r!=KErrAlreadyExists,Fault(EFileDuplicateLock)); + + return r; + } + +//--------------------------------------------------------------------------------------------------------------------- +/** + Remove a lock on a range. +*/ +TInt CFileCB::RemoveLock64(CFileShare* aFileShare, TInt64 aPos, TInt64 aLength) + { + const TUint64 posHigh = aPos+aLength-1; + + {//-- Lock overflow check + const TUint64 KMaxFileSize = aFileShare->IsFileModeBig() ? MaxSupportedSize() : KMaxLegacyFileSize; + if(posHigh > KMaxFileSize) + return KErrArgument; + } + + const TInt pos=FindLock64(aPos, posHigh); + if (pos==KErrNotFound) + return KErrNotFound; + + const TFileShareLock& lock=FileLocks()[pos]; + if (!lock.MatchOwner(aFileShare) || lock.PosLow() != (TUint64)aPos || lock.PosHigh() != posHigh) + return KErrNotFound; + + + FileLocks().Remove(pos); + + return KErrNone; + } + +//--------------------------------------------------------------------------------------------------------------------- +/** + Check if a range is available. + @param aFileShare pointer to FileShare object. NULL only when is called from CFileCB::AddLock64() + +*/ +TInt CFileCB::CheckLock64(CFileShare* aFileShare,TInt64 aPos,TInt64 aLength) + { + const TUint64 posHigh=aPos+aLength-1; + + //-- Lock overflow check. It is OK to have a lock that is beyond the real file size. + //-- if aFileShare == NULL, this is the call from AddLock64 and the position is already checked. + if(aFileShare) + { + const TUint64 KMaxFileSize = aFileShare->IsFileModeBig() ? MaxSupportedSize() : KMaxLegacyFileSize; + if(posHigh > KMaxFileSize) + return KErrNone; //-- OK, there can't be any locks beyond the max. supported file length + } + + + TInt lockIdx=FindLock64(aPos, posHigh); + if (lockIdx == KErrNotFound) + return KErrNone; + + const TInt count=FileLocks().Count(); + const TFileShareLock* lock=(&FileLocks()[lockIdx]); + + for(;;) + { + if (!lock->MatchOwner(aFileShare)) + return KErrLocked; + + if (lock->PosHigh() >= posHigh) + break; + + lockIdx++; + if (lockIdx >= count) + break; + + lock=&FileLocks()[lockIdx]; + + if (posHigh < lock->PosLow()) + break; + } + + return KErrNone; + } + + +//##################################################################################################################### +//# TFileShareLock class implementation +//##################################################################################################################### + +TFileShareLock::TFileShareLock(const CFileShare* aOwner, TUint64 aPosLow, TUint64 aPosHigh) + : iOwner(aOwner), iPosLow(aPosLow), iPosHigh(aPosHigh) + { + } + + + +TUint64 TFileShareLock::PosLow() const + { + return iPosLow; + } + + +TUint64 TFileShareLock::PosHigh() const + { + return iPosHigh; + } + +TBool TFileShareLock::MatchOwner(const CFileShare* aShare) const + { + return (aShare == iOwner); + } + +/** + @return ETrue if aPosLow and PosHigh match the lock boundaries +*/ +TBool TFileShareLock::MatchByPos(TUint64 aPosLow, TUint64 aPosHigh) const + { + if(PosLow() > aPosHigh) + return EFalse; + + if ((aPosLow >= PosLow() && aPosLow <= PosHigh()) || + (aPosHigh >= PosLow() && aPosHigh <= PosHigh()) || + (aPosLow <= PosLow() && aPosHigh >= PosHigh() )) + { + return ETrue; + } + + return EFalse; + } + + + + + + + + + + + + + + +