/* ************************************************************************ Copyright (c) 1996-1997 Microsoft Corporation Module Name: gpcdb.c Abstract: This file contains database routines, that includes specific patterns, and classification index table. Author: Ofer Bar - April 15, 1997 Environment: Kernel mode Revision History: ************************************************************************ */ #include "gpcpre.h" VOID GpcSpecificCallback( IN VOID *Ctx, IN SpecificPatternHandle SpHandle); /* ************************************************************************ InitSpecificPatternDb - Initialize the specific patterns database. It will allocate a table length Size. Arguments pDb - a pointer to the db to initialize Size - number of entries in the table Returns NDIS_STATUS ************************************************************************ */ GPC_STATUS InitSpecificPatternDb( IN PSPECIFIC_PATTERN_DB pDb, IN ULONG PatternSize ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; ULONG Len, i; TRACE(INIT, pDb, PatternSize, "InitSpecificPatternDb"); ASSERT(pDb); ASSERT(PatternSize); // // init the specific db struct // call the PH init routine // INIT_LOCK(&pDb->Lock); AllocatePatHashTable(pDb->pDb); if (pDb->pDb != NULL) { constructPatHashTable(pDb->pDb, PatternSize * 8, 2, // usage_ratio, 1, // usage_histeresis, 1, // allocation_histeresis, 16 // max_free_list_size ); } else { Status = GPC_STATUS_RESOURCES; } TRACE(INIT, Status, 0, "InitSpecificPatternDb==>"); return Status; } /* ************************************************************************ UninitSpecificPatternDb - Un-Initialize the specific patterns database. It will release all allocated memory. Arguments pDb - a pointer to the db to free Returns NDIS_STATUS ************************************************************************ */ GPC_STATUS UninitSpecificPatternDb( IN PSPECIFIC_PATTERN_DB pDb ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; TRACE(INIT, pDb, 0, "UninitSpecificPatternDb"); ASSERT(pDb); destructPatHashTable(pDb->pDb); FreePatHashTable(pDb->pDb); TRACE(INIT, Status, 0, "UninitSpecificPatternDb==>"); return Status; } /* ************************************************************************ InitClassificationHandleTbl - Init the classification index table Arguments ppCHTable - a pointer a class handle table pointer Returns GPC_STATUS ************************************************************************ */ GPC_STATUS InitClassificationHandleTbl( IN HandleFactory **ppCHTable ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; TRACE(INIT, ppCHTable, 0, "InitClassificationHandleTbl"); ASSERT(ppCHTable); NEW_HandleFactory(*ppCHTable); if (*ppCHTable == NULL) { return GPC_STATUS_RESOURCES; } if (0 != constructHandleFactory(*ppCHTable)) { return GPC_STATUS_RESOURCES; } TRACE(INIT, Status, 0, "InitClassificationIndexTbl==>"); return Status; } /* ************************************************************************ UninitClassificationHandleTbl - Uninit the classification index table Arguments pCHTable - a pointer a class handle table Returns void ************************************************************************ */ VOID UninitClassificationHandleTbl( IN HandleFactory *pCHTable ) { destructHandleFactory(pCHTable); FreeHandleFactory(pCHTable); } /* ************************************************************************ InitializeGenericDb - Init the generic db. This is called by per CF. Arguments pGenericDb - a pointer to the generic db NumEntries - number of entries, one per rhizome PatternSize - pattern size in bytes Returns GPC_STATUS: no memory resources ************************************************************************ */ GPC_STATUS InitializeGenericDb( IN PGENERIC_PATTERN_DB *ppGenericDb, IN ULONG NumEntries, IN ULONG PatternSize ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; ULONG i; PGENERIC_PATTERN_DB pDb; *ppGenericDb = NULL; ASSERT(PatternSize); GpcAllocMem(&pDb, sizeof(GENERIC_PATTERN_DB) * NumEntries, GenPatternDbTag); if (pDb == NULL) return GPC_STATUS_RESOURCES; *ppGenericDb = pDb; for (i = 0; i < NumEntries; i++, pDb++) { INIT_LOCK(&pDb->Lock); AllocateRhizome(pDb->pRhizome); if (pDb->pRhizome == NULL) { // // failed, release all allocated resources // while (i > 0) { NdisFreeSpinLock(&pDb->Lock); i--; pDb--; destructRhizome(pDb->pRhizome); FreeRhizome(pDb->pRhizome); } GpcFreeMem((*ppGenericDb), GenPatternDbTag); Status = GPC_STATUS_RESOURCES; *ppGenericDb = NULL; break; } // // init the rhizome // constructRhizome(pDb->pRhizome, PatternSize*8); } return Status; } /* ************************************************************************ UninitializeGenericDb - Uninit the generic db. Arguments pGenericDb - a pointer to the generic db NumEntries - number of entries, one per rhizome PatternSize - pattern size in bytes Returns void ************************************************************************ */ VOID UninitializeGenericDb( IN PGENERIC_PATTERN_DB *ppGenericDb, IN ULONG NumEntries ) { ULONG i; PGENERIC_PATTERN_DB pDb; pDb = *ppGenericDb; ASSERT(pDb); for (i = 0; i < NumEntries; i++, pDb++) { NdisFreeSpinLock(&pDb->Lock); destructRhizome(pDb->pRhizome); FreeRhizome(pDb->pRhizome); } GpcFreeMem(*ppGenericDb, GenPatternDbTag); *ppGenericDb = NULL; } /* ************************************************************************ GpcSpecificCallback - Call back routine given when calling scanPatHashTable and getting called by the pathash scanning routine. Arguments Ctx - a pointer to a SCAN_STRUCT to hold context info SpHandle - the specific pattern handle that matches Returns void ************************************************************************ */ VOID GpcSpecificCallback( IN VOID *Ctx, IN SpecificPatternHandle SpHandle) { PSCAN_STRUCT pScan = (PSCAN_STRUCT)Ctx; PPATTERN_BLOCK pSpPattern; PPATTERN_BLOCK pGpPattern; PGENERIC_PATTERN_DB pGenericDb; PatternHandle GpHandle; PBLOB_BLOCK pSpBlob, *ppSpCbBlob, OldBlob; ULONG CfIndex; PCF_BLOCK pCf; KIRQL ReadIrql; KIRQL CBirql; PCLASSIFICATION_BLOCK pCB; PGPC_IP_PATTERN pIp; BOOLEAN bBetterFound = FALSE; UINT i; TRACE(PATTERN, Ctx, SpHandle, "GpcSpecificCallback"); pSpPattern = (PPATTERN_BLOCK)GetReferenceFromSpecificPatternHandle(SpHandle); pCB = pSpPattern->pClassificationBlock; pIp = (PGPC_IP_PATTERN) GetKeyPtrFromSpecificPatternHandle(SpHandle); ASSERT(pCB); ASSERT(pScan); // // get the CF index // CfIndex = pScan->pClientBlock->pCfBlock->AssignedIndex; pCf = pScan->pClientBlock->pCfBlock; // // the blob that actually belongs to the SP // pSpBlob = GetBlobFromPattern(pSpPattern,CfIndex); // // the blob that currently exist in the CfIndex entry of the the CB // ppSpCbBlob = &pCB->arpBlobBlock[CfIndex]; TRACE(PATTERN, pSpBlob, *ppSpCbBlob, "GpcSpecificCallback (2)"); TRACE(PATTERN, pCB, CfIndex, "GpcSpecificCallback (2.5)"); if (pSpBlob != *ppSpCbBlob || pSpBlob == NULL) { if (!pScan->bRemove) { // // we just added the generic pattern, so we should set the // CB pointer for that CF point to the new blob // for (i = 0; i <= pScan->pPatternBlock->Priority; i++) { pGenericDb = &pScan->pClientBlock->pCfBlock->arpGenericDb[pSpPattern->ProtocolTemplate][i]; READ_LOCK(&pGenericDb->Lock, &ReadIrql); GpHandle = searchRhizome(pGenericDb->pRhizome, GetKeyPtrFromSpecificPatternHandle(SpHandle) ); TRACE(PATTERN, pGenericDb, GpHandle, "GpcSpecificCallback (3.5)"); if (GpHandle != NULL) { WRITE_LOCK(&glData.ChLock, &CBirql); bBetterFound = TRUE; pGpPattern = (PPATTERN_BLOCK)GetReferenceFromPatternHandle(GpHandle); *ppSpCbBlob = GetBlobFromPattern(pGpPattern, CfIndex); WRITE_UNLOCK(&glData.ChLock, CBirql); READ_UNLOCK(&pGenericDb->Lock, ReadIrql); break; } READ_UNLOCK(&pGenericDb->Lock, ReadIrql); } if (!bBetterFound) { WRITE_LOCK(&glData.ChLock, &CBirql); *ppSpCbBlob = pScan->pBlobBlock; WRITE_UNLOCK(&glData.ChLock, CBirql); } } else { // // The CfIndex slot in the CB points to a blob that doesn't belong // to the specific pattern we have just found. There is a chance // that there is another generic pattern somewhere, that may // or may not be more specific, thus resulting in updating the blob // pointer in the CB. So we need to search the generic db for a // match (to the specific pattern). // for (i = 0; i <= pScan->pPatternBlock->Priority; i++) { pGenericDb = &pScan->pClientBlock->pCfBlock->arpGenericDb[pSpPattern->ProtocolTemplate][i]; READ_LOCK(&pGenericDb->Lock, &ReadIrql); GpHandle = searchRhizome(pGenericDb->pRhizome, GetKeyPtrFromSpecificPatternHandle(SpHandle) ); TRACE(PATTERN, pGenericDb, GpHandle, "GpcSpecificCallback (3.5)"); if (GpHandle != NULL) { // // we found a generic pattern in the rhizoe that can also be // the same one that is currently being installed, but // that's fine, since we want the most specific one, and // the search guarantees that. // all we need to do is to update the CB of the SP with // the blob of the GP we've just found. // bBetterFound = TRUE; WRITE_LOCK(&glData.ChLock, &CBirql); OldBlob = *ppSpCbBlob; pGpPattern = (PPATTERN_BLOCK)GetReferenceFromPatternHandle(GpHandle); *ppSpCbBlob = GetBlobFromPattern(pGpPattern, CfIndex); TRACE(PATTERN, pGpPattern, pCB->arpBlobBlock[CfIndex], "GpcSpecificCallback (4)"); WRITE_UNLOCK(&glData.ChLock, CBirql); READ_UNLOCK(&pGenericDb->Lock, ReadIrql); break; } READ_UNLOCK(&pGenericDb->Lock, ReadIrql); } if (!bBetterFound) { // // non was found // WRITE_LOCK(&glData.ChLock, &CBirql); *ppSpCbBlob = NULL; WRITE_UNLOCK(&glData.ChLock, CBirql); TRACE(PATTERN, *ppSpCbBlob, pCB->arpBlobBlock[CfIndex], "GpcSpecificCallback (5)"); } } } TRACE(PATTERN, pCB, CfIndex, "GpcSpecificCallback==>"); } /* ************************************************************************ AddGenericPattern - Add a generic pattern to the db. Arguments pClient - Pattern - Mask - Priority - pBlob - ppPatter - Returns GPC_STATUS ************************************************************************ */ GPC_STATUS AddGenericPattern( IN PCLIENT_BLOCK pClient, IN PUCHAR pPatternBits, IN PUCHAR pMaskBits, IN ULONG Priority, IN PBLOB_BLOCK pBlob, IN PPROTOCOL_BLOCK pProtocol, IN OUT PPATTERN_BLOCK *ppPattern ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PatternHandle GpHandle; PPATTERN_BLOCK pPattern = *ppPattern; PGENERIC_PATTERN_DB pGenericDb; PSPECIFIC_PATTERN_DB pSpecificDb; SCAN_STRUCT ScanStruct; ULONG i; ULONG CfIndex = pClient->pCfBlock->AssignedIndex; KIRQL ReadIrql; KIRQL WriteIrql; PGPC_IP_PATTERN pIp, pMask; TRACE(PATTERN, pClient, pPatternBits, "AddGenericPattern"); // This is impossible with a good client (320705) if (!pBlob) { return GPC_STATUS_INVALID_PARAMETER; } pIp = (PGPC_IP_PATTERN)pPatternBits; pMask = (PGPC_IP_PATTERN)pMaskBits; // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); // // Add to the Rhizome tree, according to priority value // pGenericDb = &pClient->pCfBlock->arpGenericDb[pProtocol->ProtocolTemplate][Priority]; // // Lock the generic db for insertion // WRITE_LOCK(&pGenericDb->Lock, &WriteIrql); GpHandle = insertRhizome(pGenericDb->pRhizome, pPatternBits, pMaskBits, (PVOID)*ppPattern, (PULONG)&Status ); WRITE_UNLOCK(&pGenericDb->Lock, WriteIrql); if (NT_SUCCESS(Status)) { // // add one ref count // REFADD(&(*ppPattern)->RefCount, 'ADGP'); // // we managed to insert the pattern, no conflicts. // now we need to scan the specific db for a match, // since the insertion might affect CB entries for // patterns which are subsets of the installed pattern // ProtocolStatInc(pProtocol->ProtocolTemplate, InsertedRz); // // lock the specific db, some other client may access it // ScanStruct.Priority = Priority; ScanStruct.pClientBlock = pClient; ScanStruct.pPatternBlock = *ppPattern; ScanStruct.pBlobBlock = pBlob; ScanStruct.bRemove = FALSE; // // update the pattern // GetBlobFromPattern(pPattern,CfIndex) = pBlob; pPattern->pClientBlock = pClient; ASSERT(GpHandle); pPattern->DbCtx = (PVOID)GpHandle; TRACE(PATTERN, pPattern, GpHandle, "AddGenericPattern: DbCtx"); pPattern->State = GPC_STATE_READY; GpcInterlockedInsertTailList (&pBlob->PatternList, &pPattern->BlobLinkage[CfIndex], &pBlob->Lock ); // // this will do the rest of the work... // READ_LOCK(&pSpecificDb->Lock, &ReadIrql); scanPatHashTable( pSpecificDb->pDb, pPatternBits, pMaskBits, (PVOID)&ScanStruct, GpcSpecificCallback // see callback routine... ); READ_UNLOCK(&pSpecificDb->Lock, ReadIrql); } TRACE(PATTERN, Status, 0, "AddGenericPattern==>"); return Status; } void DeleteAutoPattern(PPATTERN_BLOCK pPattern,IN PPROTOCOL_BLOCK pProtocol) { NDIS_LOCK(&pPattern->Lock); pPattern->State = GPC_STATE_FORCE_REMOVE; pPattern->Flags |= ~PATTERN_AUTO; NDIS_LOCK(&pProtocol->PatternTimerLock[pPattern->WheelIndex]); if (!IsListEmpty(&pPattern->TimerLinkage)) { GpcRemoveEntryList(&pPattern->TimerLinkage); InitializeListHead(&pPattern->TimerLinkage); } NDIS_UNLOCK(&pProtocol->PatternTimerLock[pPattern->WheelIndex]); ProtocolStatInc(pPattern->ProtocolTemplate, DeletedAp); ProtocolStatDec(pPattern->ProtocolTemplate, CurrentAp); NDIS_UNLOCK(&pPattern->Lock); // // actually remove the pattern // // This will try to reacquire lock on pattern. So we released the lock above. privateGpcRemovePattern((GPC_HANDLE)pPattern->pAutoClient, (GPC_HANDLE)pPattern, TRUE, TRUE); InterlockedDecrement(&pProtocol->AutoSpecificPatternCount); } /* ************************************************************************ AddSpecificPattern - Add a specific pattern to the db. Arguments pClient - pPatternBits- pMaskBits - pBlob - pProtocol - ppPattern - ppCB - Returns GPC_STATUS ************************************************************************ */ GPC_STATUS AddSpecificPattern( IN PCLIENT_BLOCK pClient, IN PUCHAR pPatternBits, IN PUCHAR pMaskBits, IN PBLOB_BLOCK pBlob, // optional IN PPROTOCOL_BLOCK pProtocol, IN OUT PPATTERN_BLOCK *ppPattern, OUT PCLASSIFICATION_HANDLE pCH ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PSPECIFIC_PATTERN_DB pSpecificDb; PGENERIC_PATTERN_DB pGenericDb; ULONG Chyme; ULONG CfIndex, i; PPATTERN_BLOCK pPatternSave; PBLOB_BLOCK pBlobSave; SpecificPatternHandle SpHandle; PatternHandle GpHandle; PCLASSIFICATION_BLOCK pCB = NULL; PCF_BLOCK pCf = NULL; PLIST_ENTRY pHead, pEntry; KIRQL ReadIrql; KIRQL WriteIrql; KIRQL irql; //BOOLEAN bIsAuto; ASSERT(ppPattern); ASSERT(*ppPattern); TRACE(PATTERN, pClient, *ppPattern, "AddSpecificPattern"); *pCH = 0; //bIsAuto = TEST_BIT_ON((*ppPattern)->Flags, PATTERN_AUTO); // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); // // get the CF index // CfIndex = pClient->pCfBlock->AssignedIndex; pCf = pClient->pCfBlock; // // Since we want to take the blob lock and specific DB lock // in the same order everywhere. Take the blob lock before the specific DB // lock. GPCEnumCfInfo does the same. // if (pBlob) { NDIS_LOCK(&pBlob->Lock); } // // lock the specific db, some other client may access it // WRITE_LOCK(&pSpecificDb->Lock, &WriteIrql); // // calculate a chyme hash value for the pat -hash // Chyme = GpcCalcHash(pProtocol->ProtocolTemplate, pPatternBits); ASSERT(Chyme != (-1)); // // actually call insert directly. If the pattern already exist in the // db, the returned reference will be the one for a previously // installed pattern, so ppPattern will be different // SpHandle = insertPatHashTable( pSpecificDb->pDb, pPatternBits, Chyme, (PVOID)*ppPattern ); if (SpHandle != NULL) { // // the pattern block associated with the pattern we've just // installed, we may have gotten one that has already been // installed. // pPatternSave = GetReferenceFromSpecificPatternHandle(SpHandle); if (*ppPattern != pPatternSave) { if ((TEST_BIT_ON( pPatternSave->Flags, PATTERN_AUTO)) && (!TEST_BIT_ON(pPatternSave->Flags,PATTERN_AUTO_NOT_READY))) { PCLIENT_BLOCK pClient; NDIS_LOCK(&pPatternSave->Lock); REFADD(&pPatternSave->RefCount, 'DLAP'); NDIS_UNLOCK(&pPatternSave->Lock); //WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); pClient = pPatternSave->pAutoClient; DeleteAutoPattern(pPatternSave,pProtocol); //WRITE_LOCK(&pSpecificDb->Lock, &WriteIrql); REFDEL(&pPatternSave->RefCount, 'DLAP'); REFDEL(&(pClient->RefCount),'CLNT'); } SpHandle = insertPatHashTable( pSpecificDb->pDb, pPatternBits, Chyme, (PVOID)*ppPattern ); } if (SpHandle!=NULL) { pPatternSave = GetReferenceFromSpecificPatternHandle(SpHandle); if (*ppPattern != pPatternSave){ if (GetBlobFromPattern(pPatternSave,CfIndex) && pBlob) { // // there is a blob assigned to this entry // this is a NO NO, and will be REJECTED! // // // just a duplicate - the caller will release // one ref count in case of an error, so this // will keep the pattern around! // //NdisInterlockedIncrement(&(*ppPatternSave)->RefCount); //NDIS_UNLOCK(&pPatternSave->Lock); WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); // // Since we want to take the blob lock and specific DB lock // in the same order everywhere, release the blob lock after // the specific DB lock. GPCEnumCfInfo does the same. // if (pBlob) { NDIS_UNLOCK(&pBlob->Lock); } //TRACE(PATTERN, (*ppPattern)->RefCount, GPC_STATUS_CONFLICT, "AddSpecificPattern-->"); return GPC_STATUS_CONFLICT; } // // get the CB pointer, since // the pattern has been already created. // pCB = pPatternSave->pClassificationBlock; TRACE(PATTERN, pCB, CfIndex, "AddSpecificPattern (1.5)"); ASSERT(pCB); ASSERT(CfIndex < pCB->NumberOfElements); ASSERT(pPatternSave->DbCtx); // // increase ref count, since the caller assume there is // an extra one // REFADD(&pPatternSave->RefCount, 'ADSP'); *ppPattern = pPatternSave; // // increase client ref count // NdisInterlockedIncrement(&pPatternSave->ClientRefCount); if (pBlob) { // // now assign the slot entry in the CB to the new blob // WRITE_LOCK(&glData.ChLock, &irql); pCB->arpBlobBlock[CfIndex] = pBlob; WRITE_UNLOCK(&glData.ChLock, irql); GetBlobFromPattern(pPatternSave,CfIndex) = pBlob; TRACE(PATTERN, pPatternSave, pBlob, "AddSpecificPattern(2)"); // // Reasons for removing the blob->lock - // The lock is taken at teh start of this function. // (only to maintain the order in which locks are taken/released. // GpcInsertTailList (&pBlob->PatternList, &pPatternSave->BlobLinkage[CfIndex] ); } *pCH = pCB->ClassificationHandle; //NDIS_UNLOCK(&pPatternSave->Lock); } else { // if (*ppPattern != pPatternSave) ProtocolStatInc(pProtocol->ProtocolTemplate, InsertedPH); // // it's a new pattern - // first we need to create a CB and update the pattern and // the blob entries // REFADD(&pPatternSave->RefCount, 'ADSP'); pCB = CreateNewClassificationBlock(GPC_CF_MAX); // // This is a specific pattern, so we'll add the classification // handle for future use // WRITE_LOCK(&glData.ChLock, &irql); *pCH = (HFHandle)assign_HF_handle( glData.pCHTable, (void *)pCB ); ProtocolStatInc(pProtocol->ProtocolTemplate, InsertedCH); WRITE_UNLOCK(&glData.ChLock, irql); if (pCB && *pCH) { TRACE(CLASSHAND, pCB, pCB->ClassificationHandle, "AddSpecificPattern (CH+)"); // // got the CB, update the pattern // pCB->arpBlobBlock[CfIndex] = pBlob; GetBlobFromPattern(pPatternSave, CfIndex) = pBlob; pPatternSave->pClientBlock = pClient; pPatternSave->pClassificationBlock = pCB; pPatternSave->DbCtx = (PVOID)SpHandle; pCB->ClassificationHandle = *pCH; TRACE(PATTERN, pPatternSave, pBlob, "AddSpecificPattern(3)"); TRACE(PATTERN, pPatternSave, SpHandle, "AddSpecificPattern: DbCtx"); pPatternSave->State = GPC_STATE_READY; if (pBlob != NULL) { // // Reason for not using the Blob->Lock anymore - // The lock is taken at teh start of this function. // (only to maintain the order in which locks are taken/released. GpcInsertTailList (&pBlob->PatternList, &pPatternSave->BlobLinkage[CfIndex] ); } ASSERT(pCf); if (pProtocol->GenericPatternCount) { // // a new pattern has been created in the specific db. // the CB associated with it needs to be updated for each // CF entry (except the one we've already updated now) // we'll loop through the CF enlisted and find a match // for the specific pattern in each generic db. // pHead = &glData.CfList; pEntry = pHead->Flink; while (pEntry != pHead) { // // loop through the registered CF's // pCf = CONTAINING_RECORD(pEntry, CF_BLOCK, Linkage); pEntry = pEntry->Flink; if (pCf->AssignedIndex != CfIndex || pBlob == NULL) { // // skip the current CF only if this client installed // a CfInfo // pGenericDb = pCf->arpGenericDb[pProtocol->ProtocolTemplate]; ASSERT(pGenericDb); for (i = 0, pPatternSave = NULL; i < pCf->MaxPriorities && pPatternSave == NULL; i++, pGenericDb++) { // // scan each priority Rhizome // READ_LOCK(&pGenericDb->Lock, &ReadIrql); GpHandle = searchRhizome(pGenericDb->pRhizome, pPatternBits); if (GpHandle != NULL) { pPatternSave = (PPATTERN_BLOCK)GetReferenceFromPatternHandle(GpHandle); REFADD(&pPatternSave->RefCount, 'ADSP'); } READ_UNLOCK(&pGenericDb->Lock, ReadIrql); } if (pPatternSave != NULL) { // // found a generic match, get the reference // which is a pointer to a pattern and get the // blob pointer from it. // pCB->arpBlobBlock[pCf->AssignedIndex] = GetBlobFromPattern(pPatternSave,pCf->AssignedIndex); REFDEL(&pPatternSave->RefCount, 'ADSP'); } else { // // no generic pattern matches this specific one // pCB->arpBlobBlock[pCf->AssignedIndex] = NULL; } TRACE(PATTERN, pPatternSave, pCB->arpBlobBlock[pCf->AssignedIndex], "AddSpecificPattern(4)"); } } // while (pEntry != pHead) } // if (pProtocol->GenericPatternCount) } else { // if (pCB) // // remove from pathash table!! (#321509) // removePatHashTable( pSpecificDb->pDb, SpHandle ); REFDEL(&pPatternSave->RefCount, 'ADSP'); if (pCB) { ReleaseClassificationBlock(pCB); } if (*pCH) { FreeClassificationHandle(pClient, *pCH ); } Status = GPC_STATUS_RESOURCES; } } } else Status = GPC_STATUS_RESOURCES; } else Status=GPC_STATUS_RESOURCES; // // release the specific db lock // WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); // // Since we want to take the blob lock and specific DB lock // in the same order everywhere, release the blob lock after // the specific DB lock. GPCEnumCfInfo does the same. // if (pBlob) { NDIS_UNLOCK(&pBlob->Lock); } // // set output parameters: // ppPattern should have been set by now // TRACE(PATTERN, *ppPattern, Status, "AddSpecificPattern==>"); return Status; } /* ************************************************************************ HandleFragment - Handle an IP fragment. Arguments pClient - bFirstFrag - bLastFrag - Retu GPC_STATUS ************************************************************************ */ GPC_STATUS HandleFragment( IN PCLIENT_BLOCK pClient, IN PPROTOCOL_BLOCK pProtocol, IN BOOLEAN bFirstFrag, IN BOOLEAN bLastFrag, IN ULONG PacketId, IN OUT PPATTERN_BLOCK *ppPatternBlock, OUT PBLOB_BLOCK *ppBlob ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PFRAGMENT_DB pFragDb; SpecificPatternHandle SpHandle; KIRQL ReadIrql; KIRQL WriteIrql; KIRQL CHirql; ASSERT(ppPatternBlock); ASSERT(ppBlob); TRACE(CLASSIFY, PacketId, bFirstFrag, "HandleFragment: PacketId, bFirstFrag"); TRACE(CLASSIFY, PacketId, bLastFrag, "HandleFragment: PacketId, bLastFrag"); pFragDb = (PFRAGMENT_DB)pProtocol->pProtocolDb; if (bFirstFrag) { // // add an entry to the hash table // WRITE_LOCK(&pFragDb->Lock, &WriteIrql); SpHandle = insertPatHashTable( pFragDb->pDb, (char *)&PacketId, PacketId, (void *)*ppPatternBlock ); WRITE_UNLOCK(&pFragDb->Lock, WriteIrql); ProtocolStatInc(pProtocol->ProtocolTemplate, FirstFragsCount); } else { // // search for it // READ_LOCK(&pFragDb->Lock, &ReadIrql); SpHandle = searchPatHashTable( pFragDb->pDb, (char *)&PacketId, PacketId); if (SpHandle) { *ppPatternBlock = GetReferenceFromSpecificPatternHandle(SpHandle); READ_UNLOCK(&pFragDb->Lock, ReadIrql); //NdisInterlockedIncrement(&(*ppPatternBlock)->RefCount); if (bLastFrag) { // // remove the entry from the hash table // WRITE_LOCK(&pFragDb->Lock, &WriteIrql); removePatHashTable(pFragDb->pDb, SpHandle); WRITE_UNLOCK(&pFragDb->Lock, WriteIrql); ProtocolStatInc(pProtocol->ProtocolTemplate, LastFragsCount); } } else { // // not found // READ_UNLOCK(&pFragDb->Lock, ReadIrql); *ppPatternBlock = NULL; *ppBlob = NULL; Status = GPC_STATUS_NOT_FOUND; } } if (Status == GPC_STATUS_SUCCESS) { ASSERT(*ppPatternBlock); if (TEST_BIT_ON((*ppPatternBlock)->Flags, PATTERN_SPECIFIC)) { // // specific pattern, lookup throught the CH // READ_LOCK(&glData.ChLock, &CHirql); *ppBlob = (PBLOB_BLOCK)dereference_HF_handle_with_cb( glData.pCHTable, (*ppPatternBlock)->pClassificationBlock->ClassificationHandle, pClient->pCfBlock->AssignedIndex); READ_UNLOCK(&glData.ChLock, CHirql); } else { // // generic pattern, get the blob ptr directly // *ppBlob = GetBlobFromPattern((*ppPatternBlock), pClient->pCfBlock->AssignedIndex); } DBGPRINT(CLASSIFY, ("HandleFragment: Pattern=%X Blob=%X\n", *ppPatternBlock, *ppBlob)); } TRACE(CLASSIFY, *ppPatternBlock, *ppBlob, "HandleFragment==>"); return Status; } /* ************************************************************************ InternalSearchPattern - Arguments Returns matched pattern or NULL for none ************************************************************************ */ NTSTATUS InternalSearchPattern( IN PCLIENT_BLOCK pClientBlock, IN PPROTOCOL_BLOCK pProtocol, IN PVOID pPatternKey, OUT PPATTERN_BLOCK *pPatternBlock, OUT PCLASSIFICATION_HANDLE pClassificationHandle, IN BOOLEAN bNoCache ) { PSPECIFIC_PATTERN_DB pSpecificDb; PGENERIC_PATTERN_DB pGenericDb; PatternHandle GpHandle; SpecificPatternHandle SpHandle; PPATTERN_BLOCK pPattern; PCF_BLOCK pCf; int i; KIRQL ReadIrql; NTSTATUS Status; TRACE(CLASSIFY, pClientBlock, pPatternKey, "InternalSearchPattern:"); DBGPRINT(CLASSIFY, ("InternalSearchPattern: Client=%X \n", pClientBlock)); Status = GPC_STATUS_SUCCESS; // // start with the specific db // pSpecificDb = &pProtocol->SpecificDb; READ_LOCK(&pSpecificDb->Lock, &ReadIrql); pCf = pClientBlock->pCfBlock; SpHandle = searchPatHashTable( pSpecificDb->pDb, (char *)pPatternKey, GpcCalcHash(pProtocol->ProtocolTemplate, pPatternKey) ); if (SpHandle) { pPattern = (PPATTERN_BLOCK)GetReferenceFromSpecificPatternHandle(SpHandle); //NdisInterlockedIncrement(&pPattern->RefCount); *pClassificationHandle = (CLASSIFICATION_HANDLE)pPattern->pClassificationBlock->ClassificationHandle; TRACE(CLASSIFY, pClientBlock, *pClassificationHandle, "InternalSearchPattern (2)" ); } else { pPattern = NULL; *pClassificationHandle = 0; } READ_UNLOCK(&pSpecificDb->Lock, ReadIrql); if (pPattern == NULL) { if (bNoCache) { Status = GPC_STATUS_FAILURE; } else { // // no specific pattern, add an automagic one // Status = AddSpecificPatternWithTimer( pClientBlock, pProtocol->ProtocolTemplate, pPatternKey, &pPattern, pClassificationHandle ); DBGPRINT(CLASSIFY, ("InternalSearchPattern: Client=%X installed Pattern=%X\n", pClientBlock, pPattern)); } if (!NT_SUCCESS(Status)) { // // not found, search each generic db // for (i = 0; i < (int)pCf->MaxPriorities && pPattern == NULL; i++) { // // scan each priority Rhizome // pGenericDb = &pCf->arpGenericDb[pProtocol->ProtocolTemplate][i]; READ_LOCK(&pGenericDb->Lock, &ReadIrql); GpHandle = searchRhizome(pGenericDb->pRhizome, pPatternKey); if (GpHandle != NULL) { pPattern = (PPATTERN_BLOCK)GetReferenceFromPatternHandle(GpHandle); //NdisInterlockedIncrement(&pPattern->RefCount); } READ_UNLOCK(&pGenericDb->Lock, ReadIrql); } // we had to search manually, make sure we know this in the main code. *pClassificationHandle = 0; } DBGPRINT(CLASSIFY, ("InternalSearchPattern: Client=%X Generic Pattern=%X\n", pClientBlock, pPattern)); } TRACE(CLASSIFY, pPattern, *pClassificationHandle, "InternalSearchPattern==>"); DBGPRINT(CLASSIFY, ("InternalSearchPattern: Client=%X returned Pattern=%X\n", pClientBlock, pPattern)); *pPatternBlock = pPattern; return Status; } GPC_STATUS InitFragmentDb( IN PFRAGMENT_DB *ppFragDb ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PFRAGMENT_DB pDb; ULONG Len, i; TRACE(INIT, ppFragDb, 0, "InitFragmentDb"); ASSERT(ppFragDb); // // init the pattern db struct // call the PH init routine // GpcAllocMem(ppFragDb, sizeof(FRAGMENT_DB), FragmentDbTag); if (pDb = *ppFragDb) { INIT_LOCK(&pDb->Lock); AllocatePatHashTable(pDb->pDb); if (pDb->pDb != NULL) { constructPatHashTable(pDb->pDb, sizeof(ULONG), 2, // usage_ratio, 1, // usage_histeresis, 1, // allocation_histeresis, 16 // max_free_list_size ); } else { GpcFreeMem (*ppFragDb, FragmentDbTag); Status = GPC_STATUS_RESOURCES; } } else { Status = GPC_STATUS_RESOURCES; } TRACE(INIT, Status, 0, "InitFragmentDb==>"); return Status; } GPC_STATUS UninitFragmentDb( IN PFRAGMENT_DB pFragDb ) { destructPatHashTable (pFragDb->pDb); FreePatHashTable(pFragDb->pDb); GpcFreeMem (pFragDb, FragmentDbTag); return STATUS_SUCCESS; } /* ************************************************************************ RemoveSpecificPattern - Remove a specific pattern from the db. Arguments pClient - pPattern - Returns GPC_STATUS ************************************************************************ */ GPC_STATUS RemoveSpecificPattern( IN PCLIENT_BLOCK pClient, IN PPROTOCOL_BLOCK pProtocol, IN PPATTERN_BLOCK pPattern, IN BOOLEAN ForceRemoval, IN BOOLEAN DbLocked ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PSPECIFIC_PATTERN_DB pSpecificDb; PatternHandle GpHandle; PPATTERN_BLOCK pGp; int i; PBLOB_BLOCK pBlob, pNewBlob; PGENERIC_PATTERN_DB pGenericDb; KIRQL ReadIrql; KIRQL WriteIrql; ULONG ProtocolTemplate; KIRQL irql; LONG cClientRef; BOOLEAN bRemoveLinks = FALSE; GPC_HANDLE ClHandle = NULL; TRACE(PATTERN, pClient, pPattern, "RemoveSpecificPattern"); // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); ProtocolTemplate = pProtocol->ProtocolTemplate; // The plan: Remove the DbCtx (from the Specific pattern structure) // from teh Pathash table with the Specific Db Lock held. This // will ensure that if the same pattern is being added, the pathash // table will accept the new one instead of bumping up the ref on what // we are trying to delete now. // NDIS_LOCK(&pPattern->Lock); // If Database is not already locked if (!DbLocked) WRITE_LOCK(&pSpecificDb->Lock, &WriteIrql); cClientRef = NdisInterlockedDecrement(&pPattern->ClientRefCount); if (pPattern->State != GPC_STATE_DELETE) { ASSERT(cClientRef >= 0); ASSERT(pPattern->DbCtx); if (0 == cClientRef) { pPattern->State = GPC_STATE_DELETE; removePatHashTable( pSpecificDb->pDb, (SpecificPatternHandle)pPattern->DbCtx ); pPattern->DbCtx = NULL; // If Database was not locked before this function was called if (!DbLocked) WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); NDIS_UNLOCK(&pPattern->Lock); ReadySpecificPatternForDeletion( pClient, pProtocol, pPattern, DbLocked ); } else if (cClientRef > 0) { if (!DbLocked) { // If Database was not locked before this function was called WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); } NDIS_UNLOCK(&pPattern->Lock); ClientRefsExistForSpecificPattern( pClient, pProtocol, pPattern, DbLocked ); } else { // we shouldn't be getting here - really. // If Database was not locked before this function was called if (!DbLocked) WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); NDIS_UNLOCK(&pPattern->Lock); } } else { // If Database was not locked before this function was called if (!DbLocked) WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); NDIS_UNLOCK(&pPattern->Lock); } TRACE(PATTERN, pPattern, Status, "RemoveSpecificPattern==>"); return Status; } /* ************************************************************************ ReadySpecificPatternForDeletion - Remove a specific pattern from the db. Arguments pClient - pPattern - Returns GPC_STATUS ************************************************************************ */ VOID ReadySpecificPatternForDeletion( IN PCLIENT_BLOCK pClient, IN PPROTOCOL_BLOCK pProtocol, IN PPATTERN_BLOCK pPattern, IN BOOLEAN DbLocked ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PSPECIFIC_PATTERN_DB pSpecificDb; PatternHandle GpHandle; PPATTERN_BLOCK pGp; PCF_BLOCK pCf; PCLASSIFICATION_BLOCK pCB; int i; ULONG CfIndex; PBLOB_BLOCK pBlob, pNewBlob; PGENERIC_PATTERN_DB pGenericDb; KIRQL ReadIrql; KIRQL WriteIrql; ULONG ProtocolTemplate; KIRQL irql; PVOID Key; LONG cClientRef; BOOLEAN bRemoveLinks = FALSE; GPC_HANDLE ClHandle = NULL; TRACE(PATTERN, pClient, pPattern, "ReadySpecificPatternForDeletion"); // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); pCf = pClient->pCfBlock; CfIndex = pCf->AssignedIndex; pCB = pPattern->pClassificationBlock; ProtocolTemplate = pProtocol->ProtocolTemplate; Key = GetKeyPtrFromSpecificPatternHandle(((SpecificPatternHandle)pPattern->DbCtx)); ASSERT(pCB); // // Remove the ClHandle, so that if we are coming back in via a // user mode ioctl, we wont try to remove it again. // ClHandle = (HANDLE) LongToPtr(InterlockedExchange((PLONG32)&pPattern->ClHandle, 0)); if (ClHandle) { FreeHandle(ClHandle); } // // Remove the pattern from the blobs linked list. ClearPatternLinks(pPattern, pProtocol, CfIndex); // // We are going to access the Specific DB now, lock it NOW. // This should fix deadlock 248352 [ShreeM] // // If the database is not already locked if (!DbLocked) WRITE_LOCK(&pSpecificDb->Lock, &WriteIrql); // // this is the last client that holds the pattern, // we need to take the pattern off the specific db // TRACE(PATTERN, pPattern, pPattern->DbCtx, "ReadySpecificPatternForDeletion: DbCtx"); ASSERT(!pPattern->DbCtx); ProtocolStatInc(ProtocolTemplate, RemovedPH); // // free the classification handle - // this must come *before* we free the classification block // since it may be referenced by other clients // TRACE(PATTERN, pCB, CfIndex, "ReadySpecificPatternForDeletion: (2)"); FreeClassificationHandle( pClient, (CLASSIFICATION_HANDLE)pCB->ClassificationHandle ); ProtocolStatInc(ProtocolTemplate, RemovedCH); // If the database is not already locked if (!DbLocked) WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); // // bye bye pattern, at least for this client // REFDEL(&pPattern->RefCount, 'ADSP'); TRACE(PATTERN, pClient, pPattern, "ReadySpecificPatternForDeletion--------->"); } /* ************************************************************************ ClientRefsExistForSpecificPattern - Arguments pClient - pPattern - Returns GPC_STATUS ************************************************************************ */ VOID ClientRefsExistForSpecificPattern( IN PCLIENT_BLOCK pClient, IN PPROTOCOL_BLOCK pProtocol, IN PPATTERN_BLOCK pPattern, IN BOOLEAN dbLocked ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PSPECIFIC_PATTERN_DB pSpecificDb; PatternHandle GpHandle; PPATTERN_BLOCK pGp; PCF_BLOCK pCf; PCLASSIFICATION_BLOCK pCB; int i; ULONG CfIndex; PBLOB_BLOCK pBlob, pNewBlob; PGENERIC_PATTERN_DB pGenericDb; KIRQL ReadIrql; KIRQL WriteIrql; ULONG ProtocolTemplate; KIRQL irql; PVOID Key; LONG cClientRef; BOOLEAN bRemoveLinks = FALSE; GPC_HANDLE ClHandle = NULL; TRACE(PATTERN, pClient, pPattern, "ClientRefsExistForSpecificPattern"); // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); pCf = pClient->pCfBlock; CfIndex = pCf->AssignedIndex; pCB = pPattern->pClassificationBlock; ProtocolTemplate = pProtocol->ProtocolTemplate; Key = GetKeyPtrFromSpecificPatternHandle(((SpecificPatternHandle)pPattern->DbCtx)); ASSERT(pCB); // // reference count > 0 // pBlob = pCB->arpBlobBlock[CfIndex]; TRACE(PATTERN, pPattern, pBlob, "ClientRefsExistForSpecificPattern (2)"); // // We are going to access the Specific DB now, lock it NOW. // This should fix deadlock 248352 [ShreeM] // if (!dbLocked) { WRITE_LOCK(&pSpecificDb->Lock, &WriteIrql); } if (pBlob && ((pBlob->pOwnerClient == pClient) || TEST_BIT_ON(pBlob->Flags, PATTERN_REMOVE_CB_BLOB))) { bRemoveLinks = TRUE; pNewBlob = NULL; TRACE(PATTERN, pCB, CfIndex, "ClientRefsExistForSpecificPattern (3)"); // // search the generic db for the same CF, since there is an open slot, // some other generic pattern might fill it with its own blob pointer // pGenericDb = pCf->arpGenericDb[ProtocolTemplate]; ASSERT(pGenericDb); for (i = 0, pGp = NULL; i < (int)pCf->MaxPriorities && pGp == NULL; i++) { // // scan each priority Rhizome // READ_LOCK(&pGenericDb->Lock, &ReadIrql); GpHandle = searchRhizome(pGenericDb->pRhizome, Key); if (GpHandle != NULL) { // // found a generic pattern that match this specific one. // pGp = (PPATTERN_BLOCK)GetReferenceFromPatternHandle(GpHandle); pNewBlob = GetBlobFromPattern(pGp, CfIndex); } READ_UNLOCK(&pGenericDb->Lock, ReadIrql); pGenericDb++; } // // update the classification block entry // WRITE_LOCK(&glData.ChLock, &irql); pCB->arpBlobBlock[CfIndex] = pNewBlob; WRITE_UNLOCK(&glData.ChLock, irql); TRACE(PATTERN, pGp, pCB->arpBlobBlock[CfIndex], "ClientRefsExistForSpecificPattern (4)"); } // // must first release this lock to avoid dead lock // when aquiring the Blob lock // if (!dbLocked) { WRITE_UNLOCK(&pSpecificDb->Lock, WriteIrql); } // For Autopatterns pBlob = NULL // Hence bRemoveLinks = FALSE // So we would not be acessing the client block if (bRemoveLinks) { // // remove the pattern from any linked list // ClearPatternLinks(pPattern, pProtocol, CfIndex); ASSERT(CfIndex == pBlob->pOwnerClient->pCfBlock->AssignedIndex); GetBlobFromPattern(pPattern, CfIndex) = NULL; } REFDEL(&pPattern->RefCount, 'ADSP'); TRACE(PATTERN, pClient, pPattern, "ClientRefsExistForSpecificPattern---->"); } /* ************************************************************************ RemoveGenericPattern - Remove a generic pattern from the db. Arguments pClient - pPattern - Returns GPC_STATUS ************************************************************************ */ GPC_STATUS RemoveGenericPattern( IN PCLIENT_BLOCK pClient, IN PPROTOCOL_BLOCK pProtocol, IN PPATTERN_BLOCK pPattern ) { GPC_STATUS Status = GPC_STATUS_SUCCESS; PSPECIFIC_PATTERN_DB pSpecificDb; PGENERIC_PATTERN_DB pGenericDb; PCF_BLOCK pCf; SCAN_STRUCT ScanStruct; UCHAR PatternBits[MAX_PATTERN_SIZE]; UCHAR MaskBits[MAX_PATTERN_SIZE]; ULONG i; KIRQL ReadIrql; KIRQL WriteIrql; GPC_HANDLE ClHandle = NULL; TRACE(PATTERN, pPattern, pPattern->DbCtx, "RemoveGenericPattern"); ASSERT(MAX_PATTERN_SIZE >= sizeof(GPC_IP_PATTERN)); ASSERT(MAX_PATTERN_SIZE >= sizeof(GPC_IPX_PATTERN)); // // Remove the ClHandle, so that if we are coming back in via a // user mode ioctl, we wont try to remove it again. // ClHandle = (HANDLE) LongToPtr(InterlockedExchange((PLONG32)&pPattern->ClHandle, 0)); if (ClHandle) { FreeHandle(ClHandle); } pCf = pClient->pCfBlock; ScanStruct.Priority = pPattern->Priority; ScanStruct.pClientBlock = pClient; ScanStruct.pPatternBlock = pPattern; ScanStruct.pBlobBlock = GetBlobFromPattern(pPattern, pCf->AssignedIndex); ScanStruct.bRemove = TRUE; // // get the specific db pointer // pSpecificDb = &pProtocol->SpecificDb; ASSERT(pSpecificDb); pGenericDb = &pCf->arpGenericDb[pProtocol->ProtocolTemplate][pPattern->Priority]; ASSERT(pGenericDb); // Lock the Pattern // Check it's State // Set the State if not set to REMOVE // return if state already set to REMOVE NDIS_LOCK(&pPattern->Lock); if (pPattern->State==GPC_STATE_REMOVE) { NDIS_UNLOCK(&pPattern->Lock); return Status; } else{ pPattern->State = GPC_STATE_REMOVE; NDIS_UNLOCK(&pPattern->Lock); } // // remove the pattern from any linked list // ClearPatternLinks(pPattern, pProtocol, pCf->AssignedIndex); // // copy the pattern key and mask for searching later // NDIS_LOCK(&pPattern->Lock); WRITE_LOCK(&pGenericDb->Lock, &WriteIrql); ASSERT(pPattern->DbCtx); NdisMoveMemory(PatternBits, GetKeyPtrFromPatternHandle(pGenericDb->pRhizome, pPattern->DbCtx), GetKeySizeBytes(pGenericDb->pRhizome) ); NdisMoveMemory(MaskBits, GetMaskPtrFromPatternHandle(pGenericDb->pRhizome, pPattern->DbCtx), GetKeySizeBytes(pGenericDb->pRhizome) ); // // remove the pattern from generic db // removeRhizome(pGenericDb->pRhizome, (PatternHandle)pPattern->DbCtx ); ProtocolStatInc(pProtocol->ProtocolTemplate, RemovedRz); // // This is no longer valid // pPattern->DbCtx = NULL; WRITE_UNLOCK(&pGenericDb->Lock, WriteIrql); NDIS_UNLOCK(&pPattern->Lock); // // the generic pattern has been removed, // READ_LOCK(&pSpecificDb->Lock, &ReadIrql); // // this will do the rest of the work... // scanPatHashTable( pSpecificDb->pDb, (char *)PatternBits, (char *)MaskBits, (PVOID)&ScanStruct, GpcSpecificCallback // see callback routine... ); READ_UNLOCK(&pSpecificDb->Lock, ReadIrql); // // time to go to the big hunting fields.... // REFDEL(&pPattern->RefCount, 'ADGP'); TRACE(PATTERN, pPattern, Status, "RemoveGenericPattern==>"); return Status; }