/*++ Copyright (c) 1989-2000 Microsoft Corporation Module Name: index.c Abstract: This module implements the APIs and internal functions used to access and build indexes in the database. Author: dmunsil created sometime in 1999 Revision History: several people contributed (vadimb, clupu, ...) --*/ #include "sdbp.h" #if defined(KERNEL_MODE) && defined(ALLOC_DATA_PRAGMA) #pragma data_seg() #endif // KERNEL_MODE && ALLOC_DATA_PRAGMA #if defined(KERNEL_MODE) && defined(ALLOC_PRAGMA) #pragma alloc_text(PAGE, SdbFindFirstGUIDIndexedTag) #pragma alloc_text(PAGE, SdbFindNextGUIDIndexedTag) #pragma alloc_text(PAGE, SdbFindFirstDWORDIndexedTag) #pragma alloc_text(PAGE, SdbFindNextDWORDIndexedTag) #pragma alloc_text(PAGE, SdbFindFirstStringIndexedTag) #pragma alloc_text(PAGE, SdbFindNextStringIndexedTag) #pragma alloc_text(PAGE, SdbpBinarySearchUnique) #pragma alloc_text(PAGE, SdbpBinarySearchFirst) #pragma alloc_text(PAGE, SdbpGetFirstIndexedRecord) #pragma alloc_text(PAGE, SdbpGetNextIndexedRecord) #pragma alloc_text(PAGE, SdbpPatternMatch) #pragma alloc_text(PAGE, SdbpPatternMatchAnsi) #pragma alloc_text(PAGE, SdbpKeyToAnsiString) #pragma alloc_text(PAGE, SdbpFindFirstIndexedWildCardTag) #pragma alloc_text(PAGE, SdbpFindNextIndexedWildCardTag) #pragma alloc_text(PAGE, SdbGetIndex) #pragma alloc_text(PAGE, SdbpScanIndexes) #pragma alloc_text(PAGE, SdbpGetIndex) #pragma alloc_text(PAGE, SdbMakeIndexKeyFromString) #pragma alloc_text(PAGE, SdbpTagToKey) #endif // KERNEL_MODE && ALLOC_PRAGMA TAGID SdbFindFirstGUIDIndexedTag( IN PDB pdb, IN TAG tWhich, IN TAG tKey, IN GUID* pguidName, OUT FIND_INFO* pFindInfo ) /*++ Return: void Desc: This function locates the first matching entry indexed by GUID id --*/ { TAGID tiReturn; DWORD dwFlags = 0; pFindInfo->tiIndex = SdbGetIndex(pdb, tWhich, tKey, &dwFlags); if (pFindInfo->tiIndex == TAGID_NULL) { DBGPRINT((sdlError, "SdbFindFirstGUIDIndexedTag", "Failed to find index 0x%lx key 0x%lx\n", tWhich, tKey)); return TAGID_NULL; } pFindInfo->tName = tKey; pFindInfo->pguidName = pguidName; pFindInfo->dwFlags = dwFlags; pFindInfo->ullKey = MAKEKEYFROMGUID(pguidName); tiReturn = SdbpGetFirstIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo->ullKey, pFindInfo); if (tiReturn == TAGID_NULL) { // // While this is handled properly in FindMatchingGUID we return here since // the record was not found in the index. It is not an abnormal condition. // We have just failed to find the match. Likewise, DBGPRINT is not warranted // return tiReturn; } return SdbpFindMatchingGUID(pdb, tiReturn, pFindInfo); } TAGID SdbFindNextGUIDIndexedTag( IN PDB pdb, OUT FIND_INFO* pFindInfo ) /*++ Return: The TAGID of the next GUID-indexed tag. Desc: This function finds the next entry matching a guid provided in a previous call to SdbFindNextGUIDIndexedTag --*/ { TAGID tiReturn; // // Get a preliminary match from the index. // tiReturn = SdbpGetNextIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo); if (tiReturn == TAGID_NULL) { // // This case is handled properly in SdbpFindMatchingGUID // we return here however for simplicity. // DBGPRINT is not needed since it's not an abnormal condition // return tiReturn; } return SdbpFindMatchingGUID(pdb, tiReturn, pFindInfo); } TAGID SdbFindFirstDWORDIndexedTag( IN PDB pdb, IN TAG tWhich, IN TAG tKey, IN DWORD dwName, OUT FIND_INFO* pFindInfo ) /*++ Return: BUGBUG: ? Desc: BUGBUG: what does this do ? --*/ { TAGID tiReturn; DWORD dwFlags = 0; pFindInfo->tiIndex = SdbGetIndex(pdb, tWhich, tKey, &dwFlags); if (pFindInfo->tiIndex == TAGID_NULL) { DBGPRINT((sdlError, "SdbFindFirstDWORDIndexedTag", "Failed to find index 0x%lx key 0x%lx\n", tWhich, tKey)); return TAGID_NULL; } pFindInfo->tName = tKey; pFindInfo->dwName = dwName; pFindInfo->dwFlags = dwFlags; pFindInfo->ullKey = MAKEKEYFROMDWORD(dwName); tiReturn = SdbpGetFirstIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo->ullKey, pFindInfo); if (tiReturn == TAGID_NULL) { // // While this is handled properly in FindMatchingGUID we return here since // the record was not found in the index. It is not an abnormal condition. // We have just failed to find the match. Likewise, DBGPRINT is not warranted // return tiReturn; } return SdbpFindMatchingDWORD(pdb, tiReturn, pFindInfo); } TAGID SdbFindNextDWORDIndexedTag( IN PDB pdb, OUT FIND_INFO* pFindInfo ) /*++ Return: BUGBUG: ? Desc: BUGBUG: ? --*/ { TAGID tiReturn; // // Get a preliminary match from the index. // tiReturn = SdbpGetNextIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo); if (tiReturn == TAGID_NULL) { // // This case is handled properly in SdbpFindMatchingDWORD // we return here however for simplicity. // DBGPRINT is not needed since it's not an abnormal condition // return tiReturn; } return SdbpFindMatchingDWORD(pdb, tiReturn, pFindInfo); } TAGID SdbFindFirstStringIndexedTag( IN PDB pdb, IN TAG tWhich, IN TAG tKey, IN LPCTSTR pszName, OUT FIND_INFO* pFindInfo ) /*++ Return: BUGBUG: ? Desc: BUGBUG: ? --*/ { TAGID tiReturn; DWORD dwFlags = 0; pFindInfo->tiIndex = SdbGetIndex(pdb, tWhich, tKey, &dwFlags); if (pFindInfo->tiIndex == TAGID_NULL) { DBGPRINT((sdlError, "SdbFindFirstStringIndexedTag", "Index not found 0x%lx Key 0x%lx\n", tWhich, tKey)); return TAGID_NULL; } pFindInfo->tName = tKey; pFindInfo->szName = (LPTSTR)pszName; pFindInfo->dwFlags = dwFlags; pFindInfo->ullKey = SdbMakeIndexKeyFromString(pszName); // // Get a preliminary match from the index. // tiReturn = SdbpGetFirstIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo->ullKey, pFindInfo); if (tiReturn == TAGID_NULL) { // // This is not a bug, tag was not found // return tiReturn; } DBGPRINT((sdlInfo, "SdbFindFirstStringIndexedTag", "Found tagid 0x%x\n", tiReturn)); return SdbpFindMatchingName(pdb, tiReturn, pFindInfo); } TAGID SdbFindNextStringIndexedTag( IN PDB pdb, OUT FIND_INFO* pFindInfo ) /*++ Return: BUGBUG: ? Desc: BUGBUG: ? --*/ { TAGID tiReturn; // // Get a preliminary match from the index. // tiReturn = SdbpGetNextIndexedRecord(pdb, pFindInfo->tiIndex, pFindInfo); if (tiReturn == TAGID_NULL) { // // This is not a bug, this item was not found // return tiReturn; } return SdbpFindMatchingName(pdb, tiReturn, pFindInfo); } BOOL SdbpBinarySearchUnique( IN PINDEX_RECORD pRecords, // index record ptr IN DWORD nRecords, // number of records IN ULONGLONG ullKey, // key to search for OUT DWORD* pdwIndex // index to the item ) /*++ Return: TRUE if the index to the item is found. Desc: BUGBUG: comment ? --*/ { int iLeft = 0; int iRight = (int)nRecords - 1; int i = -1; ULONGLONG ullKeyIndex; BOOL bFound = FALSE; if (iRight >= 0) { do { i = (iLeft + iRight) / 2; // middle READ_INDEX_KEY(pRecords, i, &ullKeyIndex); if (ullKey <= ullKeyIndex) { iRight = i - 1; } if (ullKey >= ullKeyIndex) { iLeft = i + 1; } } while (iRight >= iLeft); } bFound = (iLeft - iRight > 1); if (bFound) { *pdwIndex = (DWORD)i; } return bFound; } BOOL SdbpBinarySearchFirst( IN PINDEX_RECORD pRecords, IN DWORD nRecords, IN ULONGLONG ullKey, OUT DWORD* pdwIndex ) { int iLeft = 0; int iRight = (int)nRecords - 1; int i = -1; ULONGLONG ullKeyIndex = 0; ULONGLONG ullKeyIndexPrev = 0; BOOL bFound = FALSE; if (iRight < 0) { return FALSE; } do { i= (iLeft + iRight) / 2; // middle READ_INDEX_KEY(pRecords, i, &ullKeyIndex); if (ullKey == ullKeyIndex) { if (i == 0 || READ_INDEX_KEY_VAL(pRecords, i - 1, &ullKeyIndexPrev) != ullKey) { // // we are done, thank you // bFound = TRUE; break; } else { // // look in the previous record // iRight = i - 1; } } else { if (ullKey < ullKeyIndex) { iRight = i - 1; } else { iLeft = i + 1; } } } while (iRight >= iLeft); if (bFound) { *pdwIndex = (DWORD)i; } return bFound; } TAGID SdbpGetFirstIndexedRecord( IN PDB pdb, // the DB to use IN TAGID tiIndex, // the index to use IN ULONGLONG ullKey, // the key to search for OUT FIND_INFO* pFindInfo // search context ) /*++ Return: the record found, or TAGID_NULL. Desc: Looks through an index for the first record that matches the key. It returns the index record position for subsequent calls to SdbpGetNextIndexedRecord. --*/ { PINDEX_RECORD pIndexRecords; DWORD dwRecords; BOOL bFound; if (SdbGetTagFromTagID(pdb, tiIndex) != TAG_INDEX_BITS) { DBGPRINT((sdlError, "SdbpGetFirstIndexedRecord", "The tag 0x%lx is not an index tag\n", tiIndex)); return TAGID_NULL; } dwRecords = SdbGetTagDataSize(pdb, tiIndex) / sizeof(INDEX_RECORD); pIndexRecords = (INDEX_RECORD*)SdbpGetMappedTagData(pdb, tiIndex); if (pIndexRecords == NULL) { DBGPRINT((sdlError, "SdbpGetFirstIndexedRecord", "Failed to get the pointer to index data, index tagid 0x%lx\n", tiIndex)); return TAGID_NULL; } // // Check to see whether our index is "unique", if so use our search proc. // if (pFindInfo->dwFlags & SHIMDB_INDEX_UNIQUE_KEY) { bFound = SdbpBinarySearchUnique(pIndexRecords, dwRecords, ullKey, &pFindInfo->dwIndexRec); if (bFound && pFindInfo->dwIndexRec < (dwRecords - 1)) { // // We have the next rec -- retrieve the next tagid. // pFindInfo->tiEndIndex = pIndexRecords[pFindInfo->dwIndexRec + 1].tiRef; } else { // // We will have to search until eof. // pFindInfo->tiEndIndex = TAGID_NULL; } pFindInfo->tiCurrent = TAGID_NULL; } else { bFound = SdbpBinarySearchFirst(pIndexRecords, dwRecords, ullKey, &pFindInfo->dwIndexRec); } return bFound ? pIndexRecords[pFindInfo->dwIndexRec].tiRef : TAGID_NULL; } TAGID SdbpGetNextIndexedRecord( IN PDB pdb, // the DB to use IN TAGID tiIndex, // the index to use OUT FIND_INFO* pFindInfo // the find context ) /*++ Return: the record found, or TAGID_NULL. Desc: Gets the next record that matches the one found by a previous call to SdbpGetFirstIndexedRecord. --*/ { ULONGLONG ullKey; ULONGLONG ullKeyNext; PINDEX_RECORD pIndexRecords; DWORD dwRecords; TAGID tiRef = TAGID_NULL; TAGID tiThis; TAG tag, tagThis; if (SdbGetTagFromTagID(pdb, tiIndex) != TAG_INDEX_BITS) { DBGPRINT((sdlError, "SdbpGetNextIndexedRecord", "The tag 0x%lx is not an index tag\n", tiIndex)); return TAGID_NULL; } pIndexRecords = (PINDEX_RECORD)SdbpGetMappedTagData(pdb, tiIndex); if (pIndexRecords == NULL) { DBGPRINT((sdlError, "SdbpGetNextIndexedRecord", "Failed to get pointer to the index data tagid x%lx\n", tiIndex)); return TAGID_NULL; } if (pFindInfo->dwFlags & SHIMDB_INDEX_UNIQUE_KEY) { // // There are 2 cases: // - this is the very first call to SdbpGetNextIndexedrecord // - this is one of the subsequent calls // // In the first case, we will have tiCurrent member of the FIND_INFO // structure set to TAGID_NULL. We use then the reference to the // index table contained in pFindInfo->dwIndexRec to obtain the reference // to the next eligible entry in the database. // In the second case we use the stored tiCurrent to obtain the current tag // if (pFindInfo->tiCurrent == TAGID_NULL) { tiThis = pIndexRecords[pFindInfo->dwIndexRec].tiRef; } else { tiThis = pFindInfo->tiCurrent; } // // The tag tiThis which we just obtained was the one we previously looked at // we need to step to the next tag, the call below does that. Entries are sorted // since we're using "unique" index // tiRef = SdbpGetNextTagId(pdb, tiThis); // // Now check the tag for corruption, eof and other calamities. // tagThis = SdbGetTagFromTagID(pdb, tiThis); tag = SdbGetTagFromTagID(pdb, tiRef); if (tag == TAG_NULL || GETTAGTYPE(tag) != TAG_TYPE_LIST || tag != tagThis) { // // This is NOT a bug, but a special condition when the tag happened to be // the very last tag in the index, thus we have to walk until we hit either // the end of the file - or a tag of a different type return TAGID_NULL; } // // Also check for the endtag. It will be a check for TAGID_NULL if we're // looking for eof but this condition has already been caught by the code above. // if (tiRef == pFindInfo->tiEndIndex) { // // This is not an error condition. We have walked all the matching entries until // we hit the very last entry, as denoted by tiEndIndex // return TAGID_NULL; } // // Also here check whether the key still has the same // value for this entry as it did for the previous entry. // This would have been easy but keys are not immediately available // for this entry therefore we just return the tiRef. The caller will // verify whether the entry is valid and whether the search should continue. // pFindInfo->tiCurrent = tiRef; } else { dwRecords = SdbGetTagDataSize(pdb, tiIndex) / sizeof(INDEX_RECORD); // // Get out if this is the last record. // if (pFindInfo->dwIndexRec == dwRecords - 1) { // // This is not a bug, record not found // return TAGID_NULL; } // // we check the next index record to see if it has the same key // READ_INDEX_KEY(pIndexRecords, pFindInfo->dwIndexRec, &ullKey); READ_INDEX_KEY(pIndexRecords, pFindInfo->dwIndexRec + 1, &ullKeyNext); if (ullKey != ullKeyNext) { // // This is not a bug, record not found // return TAGID_NULL; } ++pFindInfo->dwIndexRec; tiRef = pIndexRecords[pFindInfo->dwIndexRec].tiRef; } return tiRef; } BOOL SdbpPatternMatch( IN LPCTSTR pszPattern, IN LPCTSTR pszTestString) /*++ Return: TRUE if pszTestString matches pszPattern FALSE if not Desc: This function does a case-insensitive comparison of pszTestString against pszPattern. pszPattern can include asterisks to do wildcard matches. Any complaints about this function should be directed toward MarkDer. --*/ { // // March through pszTestString. Each time through the loop, // pszTestString is advanced one character. // while (TRUE) { // // If pszPattern and pszTestString are both sitting on a NULL, // then they reached the end at the same time and the strings // must be equal. // if (*pszPattern == TEXT('\0') && *pszTestString == TEXT('\0')) { return TRUE; } if (*pszPattern != TEXT('*')) { // // Non-asterisk mode. Look for a match on this character. // If equal, continue traversing. Otherwise, the strings // cannot be equal so return FALSE. // if (UPCASE_CHAR(*pszPattern) == UPCASE_CHAR(*pszTestString)) { pszPattern++; } else { return FALSE; } } else { // // Asterisk mode. Look for a match on the character directly // after the asterisk. // if (*(pszPattern + 1) == TEXT('*')) { // // Asterisks exist side by side. Advance the pattern pointer // and go through loop again. // pszPattern++; continue; } if (*(pszPattern + 1) == TEXT('\0')) { // // Asterisk exists at the end of the pattern string. Any // remaining part of pszTestString matches so we can // immediately return TRUE. // return TRUE; } if (UPCASE_CHAR(*(pszPattern + 1)) == UPCASE_CHAR(*pszTestString)) { // // Characters match. If the remaining parts of // pszPattern and pszTestString match, then the entire // string matches. Otherwise, keep advancing the // pszTestString pointer. // if (SdbpPatternMatch(pszPattern + 1, pszTestString)) { return TRUE; } } } // // No more pszTestString left. Must not be a match. // if (!*pszTestString) { return FALSE; } pszTestString++; } } BOOL SdbpPatternMatchAnsi( IN LPCSTR pszPattern, IN LPCSTR pszTestString) { // // March through pszTestString. Each time through the loop, // pszTestString is advanced one character. // while (TRUE) { // // If pszPattern and pszTestString are both sitting on a NULL, // then they reached the end at the same time and the strings // must be equal. // if (*pszPattern == '\0' && *pszTestString == '\0') { return TRUE; } if (*pszPattern != '*') { // // Non-asterisk mode. Look for a match on this character. // If equal, continue traversing. Otherwise, the strings // cannot be equal so return FALSE. // if (toupper(*pszPattern) == toupper(*pszTestString)) { pszPattern++; } else { return FALSE; } } else { // // Asterisk mode. Look for a match on the character directly // after the asterisk. // if (*(pszPattern + 1) == '*') { // // Asterisks exist side by side. Advance the pattern pointer // and go through loop again. // pszPattern++; continue; } if (*(pszPattern + 1) == '\0') { // // Asterisk exists at the end of the pattern string. Any // remaining part of pszTestString matches so we can // immediately return TRUE. // return TRUE; } if (toupper(*(pszPattern + 1)) == toupper(*pszTestString)) { // // Characters match. If the remaining parts of // pszPattern and pszTestString match, then the entire // string matches. Otherwise, keep advancing the // pszTestString pointer. // if (SdbpPatternMatchAnsi(pszPattern + 1, pszTestString)) { return TRUE; } } } // // No more pszTestString left. Must not be a match. // if (!*pszTestString) { return FALSE; } pszTestString++; } } char* SdbpKeyToAnsiString( ULONGLONG ullKey, char* szString ) /*++ Return: ? Desc: ? --*/ { char* szRevString = (char*)&ullKey; int i; for (i = 0; i < 8; ++i) { szString[i] = szRevString[7 - i]; } szString[8] = 0; return szString; } TAGID SdbpFindFirstIndexedWildCardTag( PDB pdb, TAG tWhich, TAG tKey, LPCTSTR szName, FIND_INFO* pFindInfo ) /*++ Return: ? Desc: ? --*/ { char szAnsiName[MAX_PATH]; char szAnsiKey[10]; PINDEX_RECORD pIndex = NULL; DWORD dwRecs; NTSTATUS status; DWORD dwFlags = 0; DWORD i, j; pFindInfo->tiIndex = SdbGetIndex(pdb, tWhich, tKey, &dwFlags); if (pFindInfo->tiIndex == TAGID_NULL) { DBGPRINT((sdlError, "SdbpFindFirstIndexedWilCardTag", "Failed to get an index for tag 0x%lx key 0x%lx\n", (DWORD)tWhich, (DWORD)tKey)); return TAGID_NULL; } pFindInfo->tName = tKey; pFindInfo->szName = szName; pFindInfo->dwFlags = dwFlags; RtlZeroMemory(szAnsiName, MAX_PATH); RtlZeroMemory(szAnsiKey, 10); // // Get the uppercase ANSI version of this search string so // it will match the keys in the index. // status = UPCASE_UNICODETOMULTIBYTEN(szAnsiName, CHARCOUNT(szAnsiName), // this is size in characters pFindInfo->szName); if (!NT_SUCCESS(status)) { DBGPRINT((sdlError, "SdbpFindFirstIndexedWildCardTag", "Failed to convert name to multi-byte\n")); return TAGID_NULL; } // // Get the index. // pIndex = SdbpGetIndex(pdb, pFindInfo->tiIndex, &dwRecs); if (pIndex == NULL) { DBGPRINT((sdlError, "SdbpFindFirstIndexedWildCardTag", "Failed to get index by tag id 0x%lx\n", pFindInfo->tiIndex)); return TAGID_NULL; } // // Walk through the whole index sequentially, doing a first pass check of the key // so we can avoid getting the whole record if the name clearly isn't a match. // for (i = 0; i < dwRecs; ++i) { TAGID tiMatch; TAGID tiKey; LPTSTR szDBName; ULONGLONG ullKey; READ_INDEX_KEY(pIndex, i, &ullKey); // // the call below never fails, so we don't check return value // SdbpKeyToAnsiString(pIndex[i].ullKey, szAnsiKey); // // If the original pattern match is more than eight characters, we have // to plant an asterisk at the eighth character so that proper wildcard // matching occurs. // szAnsiKey[8] = '*'; // // Quick check of the string that's in the key. // if (!SdbpPatternMatchAnsi(szAnsiKey, szAnsiName)) { continue; } // // We found a tentative match, now pull the full record and // see if it's real. // tiMatch = pIndex[i].tiRef; // // Get the key field. // tiKey = SdbFindFirstTag(pdb, tiMatch, pFindInfo->tName); if (tiKey == TAGID_NULL) { // // This is not a bug, but rather continue searching // continue; } szDBName = SdbGetStringTagPtr(pdb, tiKey); if (szDBName == NULL) { // BUGBUG: what if this fails ? continue; } // // Is this really a match? // if (SdbpPatternMatch(szDBName, pFindInfo->szName)) { pFindInfo->dwIndexRec = i; return tiMatch; } } // BUGBUG: DPF return TAGID_NULL; } TAGID SdbpFindNextIndexedWildCardTag( PDB pdb, FIND_INFO* pFindInfo ) /*++ Return: ? Desc: ? --*/ { char szAnsiName[MAX_PATH]; char szAnsiKey[10]; PINDEX_RECORD pIndex = NULL; DWORD dwRecs; NTSTATUS status; DWORD i, j; RtlZeroMemory(szAnsiName, MAX_PATH); RtlZeroMemory(szAnsiKey, 10); // // Get the uppercase ANSI version of this search string so // it will match the keys in the index. // status = UPCASE_UNICODETOMULTIBYTEN(szAnsiName, CHARCOUNT(szAnsiName), pFindInfo->szName); if (!NT_SUCCESS(status)) { // BUGBUG: DPF return TAGID_NULL; } // // Get the index. // pIndex = SdbpGetIndex(pdb, pFindInfo->tiIndex, &dwRecs); if (pIndex == NULL) { // BUGBUG: DPF return TAGID_NULL; } // // Walk through the rest of the index sequentially, doing a first pass // check of the key so we can avoid getting the whole record if the // name clearly isn't a match. // for (i = pFindInfo->dwIndexRec + 1; i < dwRecs; ++i) { TAGID tiMatch; TAGID tiKey; LPTSTR pszDBName; ULONGLONG ullKey; READ_INDEX_KEY(pIndex, i, &ullKey); SdbpKeyToAnsiString(ullKey, szAnsiKey); // // If the original pattern match is more than eight characters, we have // to plant an asterisk at the eighth character so that proper wildcard // matching occurs. // szAnsiKey[8] = '*'; // // Quick check of the string that's in the key. // if (!SdbpPatternMatchAnsi(szAnsiKey, szAnsiName)) { // BUGBUG: DPF continue; } // // We found a tentative match, now pull the full record and // see if it's real. // tiMatch = pIndex[i].tiRef; // // Get the key field. // tiKey = SdbFindFirstTag(pdb, tiMatch, pFindInfo->tName); if (tiKey == TAGID_NULL) { // BUGBUG: DPF continue; } pszDBName = SdbGetStringTagPtr(pdb, tiKey); if (pszDBName == NULL) { // BUGBUG: DPF continue; } // // Is this really a match? // if (SdbpPatternMatch(pszDBName, pFindInfo->szName)) { pFindInfo->dwIndexRec = i; return tiMatch; } } // BUGBUG: DPF return TAGID_NULL; } // // Index access functions (for reading) -- better to use tiFindFirstIndexedTag, above // TAGID SdbGetIndex( IN PDB pdb, // db to use IN TAG tWhich, // tag we'd like an index for IN TAG tKey, // the kind of tag used as a key for this index OUT LPDWORD lpdwFlags // index record flags (e.g. indicator whether the index // is "unique" style ) /*++ Return: TAGID of index, or TAGID_NULL. Desc: Retrieves a TAGID ptr to the index bits for a specific tag, if one exists. --*/ { TAGID tiReturn = TAGID_NULL; int i; // // Scan the indexes if not done already. // if (!pdb->bIndexesScanned) { SdbpScanIndexes(pdb); } for (i = 0; i < MAX_INDEXES; ++i) { if (!pdb->aIndexes[i].tWhich) { DBGPRINT((sdlInfo, "SdbGetIndex", "index 0x%x(0x%x) was not found in the index table\n", tWhich, tKey)); return TAGID_NULL; } if (pdb->aIndexes[i].tWhich == tWhich && pdb->aIndexes[i].tKey == tKey) { tiReturn = pdb->aIndexes[i].tiIndex; if (lpdwFlags != NULL) { *lpdwFlags = pdb->aIndexes[i].dwFlags; } break; } } return tiReturn; } void SdbpScanIndexes( IN PDB pdb // db to use ) /*++ Params: described above. Return: void. No failure case. Desc: Scans the initial tags in the DB and gets the index pointer info. --*/ { TAGID tiFirst; TAGID tiIndex; if (pdb->bIndexesScanned && !pdb->bWrite) { // // This is not an error condition // return; } RtlZeroMemory(pdb->aIndexes, sizeof(pdb->aIndexes)); pdb->bIndexesScanned = TRUE; // // The indexes must be the first tag. // tiFirst = SdbGetFirstChild(pdb, TAGID_ROOT); if (tiFirst == TAGID_NULL) { DBGPRINT((sdlError, "SdbpScanIndexes", "Failed to get the child index from root\n")); return; } if (SdbGetTagFromTagID(pdb, tiFirst) != TAG_INDEXES) { DBGPRINT((sdlError, "SdbpScanIndexes", "Root child tag is not index tagid 0x%lx\n", tiFirst)); return; } pdb->dwIndexes = 0; tiIndex = SdbFindFirstTag(pdb, tiFirst, TAG_INDEX); while (tiIndex != TAGID_NULL) { TAGID tiIndexTag; TAGID tiIndexKey; TAGID tiIndexBits; TAGID tiIndexFlags; if (pdb->dwIndexes == MAX_INDEXES) { DBGPRINT((sdlError, "SdbpScanIndexes", "Too many indexes in file. Recompile and increase MAX_INDEXES.\n")); return; } tiIndexTag = SdbFindFirstTag(pdb, tiIndex, TAG_INDEX_TAG); if (tiIndexTag == TAGID_NULL) { DBGPRINT((sdlError, "SdbpScanIndexes", "Index missing TAG_INDEX_TAG.\n")); return; } pdb->aIndexes[pdb->dwIndexes].tWhich = SdbReadWORDTag(pdb, tiIndexTag, TAG_NULL); tiIndexKey = SdbFindFirstTag(pdb, tiIndex, TAG_INDEX_KEY); if (tiIndexKey == TAGID_NULL) { DBGPRINT((sdlError, "SdbpScanIndexes", "Index missing TAG_INDEX_KEY.\n")); return; } pdb->aIndexes[pdb->dwIndexes].tKey = SdbReadWORDTag(pdb, tiIndexKey, TAG_NULL); tiIndexFlags = SdbFindFirstTag(pdb, tiIndex, TAG_INDEX_FLAGS); if (tiIndexFlags != TAGID_NULL) { pdb->aIndexes[pdb->dwIndexes].dwFlags = SdbReadDWORDTag(pdb, tiIndexFlags, 0); } else { pdb->aIndexes[pdb->dwIndexes].dwFlags = 0; } tiIndexBits = SdbFindFirstTag(pdb, tiIndex, TAG_INDEX_BITS); if (tiIndexBits == TAGID_NULL) { pdb->aIndexes[pdb->dwIndexes].tWhich = TAG_NULL; DBGPRINT((sdlError, "SdbpScanIndexes", "Index missing TAG_INDEX_BITS.\n")); return; } pdb->aIndexes[pdb->dwIndexes].tiIndex = tiIndexBits; pdb->dwIndexes++; tiIndex = SdbFindNextTag(pdb, tiFirst, tiIndex); } return; } PINDEX_RECORD SdbpGetIndex( IN PDB pdb, IN TAGID tiIndex, OUT DWORD* pdwNumRecs ) /*++ Return: ? Desc: ? --*/ { if (SdbGetTagFromTagID(pdb, tiIndex) != TAG_INDEX_BITS) { DBGPRINT((sdlError, "SdbpGetIndex", "Index tagid 0x%lx is not referring to the index bits\n", tiIndex)); return NULL; } *pdwNumRecs = SdbGetTagDataSize(pdb, tiIndex) / sizeof(INDEX_RECORD); return (PINDEX_RECORD)SdbpGetMappedTagData(pdb, tiIndex); } #if defined(_WIN64) ULONGLONG SdbMakeIndexKeyFromGUID( IN GUID* pGuid ) /* Return: a 64-bit key to use for searching Desc: The standard index key is created for a Guid using the xor operation on a first and second half of guid */ { ULONGLONG ullPart1 = 0, ullPart2 = 0; RtlMoveMemory(&ullPart1, pGuid, sizeof(ULONGLONG)); RtlMoveMemory(&ullPart2, (PBYTE)pGuid + sizeof(ULONGLONG), sizeof(ULONGLONG)); return (ullPart1 ^ ullPart2); } #endif // _WIN64 #define SDB_KEY_LENGTH_BYTES 8 #define SDB_KEY_LENGTH 8 ULONGLONG SdbMakeIndexKeyFromString( IN LPCTSTR szKey ) /*++ Return: a 64-bit key to use for searching. Desc: The standard index key for a Unicode string is the first 8 characters of the string, converted to uppercase ansi, then cast to a ULONGLONG (64 bit unsigned int). --*/ { char szFlippedKey[SDB_KEY_LENGTH_BYTES]; // flipped to deal with little-endian issues char* pszKey = &szFlippedKey[SDB_KEY_LENGTH_BYTES-1]; // points to the last char NTSTATUS status; int i; WCHAR ch; int nLength; #ifndef WIN32A_MODE UNICODE_STRING ustrKey; UNICODE_STRING ustrKeySrc; // truncated string UNICODE_STRING ustrKeySrcUpcased; WCHAR Buffer[SDB_KEY_LENGTH]; WCHAR BufferUpcased[SDB_KEY_LENGTH]; LPCWSTR pKeyBuffer = BufferUpcased; NTSTATUS Status; RtlInitUnicodeString(&ustrKey, szKey); // // Call below copies upto maximum length of the destination string // ustrKeySrc.Buffer = Buffer; ustrKeySrc.MaximumLength = sizeof(Buffer); RtlCopyUnicodeString(&ustrKeySrc, &ustrKey); // // Upcase what we have created // ustrKeySrcUpcased.Buffer = BufferUpcased; ustrKeySrcUpcased.MaximumLength = sizeof(BufferUpcased); Status = RtlUpcaseUnicodeString(&ustrKeySrcUpcased, &ustrKeySrc, FALSE); if (!NT_SUCCESS(Status)) { DBGPRINT((sdlError, "SdbMakeIndexKeyFromString", "Failed to upcase unicode string \"%s\"\n", szKey)); return 0; } // // Now we have an upper-case unicode string which is of max. 8 characters length // nLength = ustrKeySrcUpcased.Length / sizeof(WCHAR); #else // WIN32A_MODE WCHAR Buffer[SDB_KEY_LENGTH + 1]; LPCWSTR pKeyBuffer = Buffer; nLength = mbstowcs(Buffer, szKey, CHARCOUNT(Buffer)); if (nLength < 0) { DBGPRINT((sdlError, "SdbMakeIndexKeyFromString", "Failed to convert string \"%s\" to unicode\n", szKey)); return 0; } Buffer[nLength] = TEXT('\0'); // zero-terminate // // Upcase now. Buffer is always 0-terminated. // _wcsupr(Buffer); #endif // WIN32A_MODE assert(nLength <= SDB_KEY_LENGTH); RtlZeroMemory(szFlippedKey , sizeof(szFlippedKey)); // // To be compatible with the old (ANSI) scheme of making keys, we // construct the key using all non-null bytes in the string, up to 8 // for (i = 0; i < nLength; ++i) { ch = *pKeyBuffer++; *pszKey-- = (unsigned char)ch; // // ch is a unicode char, whatever it is, see if it has 2 bytes or just one // if (HIBYTE(ch) && i < (SDB_KEY_LENGTH - 1)) { // // Two bytes, store both // *pszKey-- = (unsigned char)HIBYTE(ch); ++i; } } return *((ULONGLONG*)szFlippedKey); } ULONGLONG SdbpTagToKey( IN PDB pdb, IN TAGID tiTag ) /*++ Return: ? Desc: ? --*/ { TAG_TYPE ttType; ULONGLONG ullReturn = 0; DWORD dwSize; PVOID pData; LPTSTR szTemp = NULL; ttType = GETTAGTYPE(SdbGetTagFromTagID(pdb, tiTag)); switch (ttType) { case TAG_TYPE_STRING: case TAG_TYPE_STRINGREF: szTemp = SdbGetStringTagPtr(pdb, tiTag); if (!szTemp) { ullReturn = 0; } else { ullReturn = SdbMakeIndexKeyFromString(szTemp); } break; case TAG_TYPE_NULL: ullReturn = 1; break; case TAG_TYPE_BINARY: // indexing binary data // check that the size of the data is sizeof(GUID) if (sizeof(GUID) == SdbGetTagDataSize(pdb, tiTag)) { // // Special case. // pData = SdbpGetMappedTagData(pdb, tiTag); if (pData == NULL) { return 0; } ullReturn = MAKEKEYFROMGUID((GUID*)pData); break; } // // Fall through to the general binary data case. // default: dwSize = SdbGetTagDataSize(pdb, tiTag); if (dwSize > sizeof(ULONGLONG)) { dwSize = sizeof(ULONGLONG); } pData = SdbpGetMappedTagData(pdb, tiTag); if (pData == NULL) { return 0; } memcpy(&ullReturn, pData, dwSize); break; } return ullReturn; }