/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * sqlitecache.c * * Abstract: * * Registry Sqlite backend caching layer APIs * * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com) * Sriram Nambakam (snambakam@likewisesoftware.com) * Wei Fu (wfu@likewise.com) */ #include "includes.h" /*Find whether this key has already been in active Key list, * If not, create key context and add the active key to the list * Otherwise, increment the existing active key reference count by 1 */ PREG_KEY_CONTEXT SqliteCacheLocateActiveKey( IN PCWSTR pwszKeyName ) { PREG_KEY_CONTEXT pKeyResult = NULL; BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gActiveKeyList.mutex); pKeyResult = SqliteCacheLocateActiveKey_inlock(pwszKeyName); LWREG_UNLOCK_MUTEX(bInLock, &gActiveKeyList.mutex); return pKeyResult; } PREG_KEY_CONTEXT SqliteCacheLocateActiveKey_inlock( IN PCWSTR pwszKeyName ) { PREG_KEY_CONTEXT pKeyResult = NULL; DWORD status = 0; status = RegHashGetValue(gActiveKeyList.pKeyList, pwszKeyName, (PVOID*)&pKeyResult); if (!status && pKeyResult) { LwInterlockedIncrement(&pKeyResult->refCount); } return pKeyResult; } NTSTATUS SqliteCacheInsertActiveKey( IN PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gActiveKeyList.mutex); status = SqliteCacheInsertActiveKey_inlock(pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_MUTEX(bInLock, &gActiveKeyList.mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheInsertActiveKey_inlock( IN PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; status = RegHashSetValue(gActiveKeyList.pKeyList, (PVOID)pKeyResult->pwszKeyName, (PVOID)pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: return status; error: goto cleanup; } VOID SqliteCacheDeleteActiveKey( IN PWSTR pwszKeyName ) { BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gActiveKeyList.mutex); SqliteCacheDeleteActiveKey_inlock(pwszKeyName); LWREG_UNLOCK_MUTEX(bInLock, &gActiveKeyList.mutex); return; } VOID SqliteCacheDeleteActiveKey_inlock( IN PWSTR pwszKeyName ) { REG_HASH_ITERATOR hashIterator; REG_HASH_ENTRY* pHashEntry = NULL; PREG_KEY_CONTEXT pKeyResult = NULL; int iCount = 0; NTSTATUS status = 0; status = RegHashGetValue(gActiveKeyList.pKeyList, pwszKeyName, (PVOID*)&pKeyResult); if (STATUS_OBJECT_NAME_NOT_FOUND == status) { return; } RegHashGetIterator(gActiveKeyList.pKeyList, &hashIterator); for (iCount = 0; (pHashEntry = RegHashNext(&hashIterator)) != NULL; iCount++) { if (LwRtlWC16StringIsEqual((PCWSTR)pHashEntry->pKey, pwszKeyName, FALSE)) { RegHashRemoveKey(gActiveKeyList.pKeyList, pHashEntry->pKey); break; } } return; } void SqliteCacheResetParentKeySubKeyInfo( IN PCWSTR pwszParentKeyName ) { PREG_KEY_CONTEXT pKeyResult = SqliteCacheLocateActiveKey(pwszParentKeyName); if (pKeyResult) { // Todo: we can reflect activeKey to have the most current information, // For now, set the bHasSubKeyInfo to false to force it grab information from db RegSrvResetSubKeyInfo(pKeyResult); } SqliteReleaseKeyContext(pKeyResult); return; } void SqliteCacheResetParentKeySubKeyInfo_inlock( IN PCWSTR pwszParentKeyName ) { PREG_KEY_CONTEXT pKeyResult = SqliteCacheLocateActiveKey_inlock(pwszParentKeyName); if (pKeyResult) { // Todo: we can reflect activeKey to have the most current information, // For now, set the bHasSubKeyInfo to false to force it grab information from db RegSrvResetSubKeyInfo(pKeyResult); } SqliteReleaseKeyContext_inlock(pKeyResult); return; } void SqliteCacheResetKeyValueInfo( IN PCWSTR pwszKeyName ) { PREG_KEY_CONTEXT pKeyResult = SqliteCacheLocateActiveKey(pwszKeyName); if (pKeyResult) { // Todo: we can reflect activeKey to have the most current information, // For now, set the bHasSubKeyInfo to false to force it grab information from db RegSrvResetValueInfo(pKeyResult); } SqliteReleaseKeyContext(pKeyResult); return; } void SqliteCacheResetKeyValueInfo_inlock( IN PCWSTR pwszKeyName ) { PREG_KEY_CONTEXT pKeyResult = SqliteCacheLocateActiveKey_inlock(pwszKeyName); if (pKeyResult) { // Todo: we can reflect activeKey to have the most current information, // For now, set the bHasSubKeyInfo to false to force it grab information from db RegSrvResetValueInfo(pKeyResult); } SqliteReleaseKeyContext_inlock(pKeyResult); return; } void SqliteSafeFreeKeyHandle( PREG_KEY_HANDLE pKeyHandle ) { if (!pKeyHandle) { return; } SqliteReleaseKeyContext(pKeyHandle->pKey); memset(pKeyHandle, 0, sizeof(*pKeyHandle)); LWREG_SAFE_FREE_MEMORY(pKeyHandle); } void SqliteSafeFreeKeyHandle_inlock( PREG_KEY_HANDLE pKeyHandle ) { if (!pKeyHandle) { return; } SqliteReleaseKeyContext_inlock(pKeyHandle->pKey); memset(pKeyHandle, 0, sizeof(*pKeyHandle)); LWREG_SAFE_FREE_MEMORY(pKeyHandle); } VOID SqliteReleaseKeyContext( PREG_KEY_CONTEXT pKeyResult ) { BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gActiveKeyList.mutex); SqliteReleaseKeyContext_inlock(pKeyResult); LWREG_UNLOCK_MUTEX(bInLock, &gActiveKeyList.mutex); } VOID SqliteReleaseKeyContext_inlock( PREG_KEY_CONTEXT pKeyResult ) { if (pKeyResult && InterlockedDecrement(&pKeyResult->refCount) == 0) { SqliteCacheDeleteActiveKey_inlock(pKeyResult->pwszKeyName); } } void SqliteCacheFreeKeyCtxHashEntry( IN const REG_HASH_ENTRY* pEntry ) { PREG_KEY_CONTEXT pKeyResult = (PREG_KEY_CONTEXT)pEntry->pValue; if (pKeyResult) { RegSrvSafeFreeKeyContext(pKeyResult); } } // sqlite caching helper functions supporting paging NTSTATUS SqliteCacheSubKeysInfo_inlock( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; size_t sNumSubKeys = 0; size_t sNumCacheSubKeys = 0; PREG_DB_KEY* ppRegEntries = NULL; if (pKeyResult->bHasSubKeyInfo) { goto cleanup; } status = RegDbQueryInfoKeyCount(ghCacheConnection, pKeyResult->qwId, QuerySubKeys, &sNumSubKeys); BAIL_ON_NT_STATUS(status); sNumCacheSubKeys = (sNumSubKeys > (size_t)dwDefaultCacheSize) ? dwDefaultCacheSize : sNumSubKeys; status = RegDbQueryInfoKey(ghCacheConnection, pKeyResult->pwszKeyName, pKeyResult->qwId, sNumCacheSubKeys, 0, &sNumCacheSubKeys, &ppRegEntries); BAIL_ON_NT_STATUS(status); status = RegDbSafeRecordSubKeysInfo_inlock( sNumSubKeys, sNumCacheSubKeys, ppRegEntries, pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: RegDbSafeFreeEntryKeyList(sNumCacheSubKeys,&ppRegEntries); return status; error: goto cleanup; } NTSTATUS SqliteCacheSubKeysInfo_inlock_inDblock( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; size_t sNumSubKeys = 0; size_t sNumCacheSubKeys = 0; PREG_DB_KEY* ppRegEntries = NULL; if (pKeyResult->bHasSubKeyInfo) { goto cleanup; } status = RegDbQueryInfoKeyCount_inlock(ghCacheConnection, pKeyResult->qwId, QuerySubKeys, &sNumSubKeys); BAIL_ON_NT_STATUS(status); sNumCacheSubKeys = (sNumSubKeys > (size_t)dwDefaultCacheSize) ? dwDefaultCacheSize : sNumSubKeys; status = RegDbQueryInfoKey_inlock(ghCacheConnection, pKeyResult->pwszKeyName, pKeyResult->qwId, sNumCacheSubKeys, 0, &sNumCacheSubKeys, &ppRegEntries); BAIL_ON_NT_STATUS(status); status = RegDbSafeRecordSubKeysInfo_inlock( sNumSubKeys, sNumCacheSubKeys, ppRegEntries, pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: RegDbSafeFreeEntryKeyList(sNumCacheSubKeys,&ppRegEntries); return status; error: goto cleanup; } NTSTATUS SqliteCacheSubKeysInfo( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; BAIL_ON_NT_INVALID_POINTER(pKeyResult); LWREG_LOCK_RWMUTEX_EXCLUSIVE(bInLock, &pKeyResult->mutex); status = SqliteCacheSubKeysInfo_inlock(pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_RWMUTEX(bInLock, &pKeyResult->mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheKeySecurityDescriptor_inlock( IN PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = 0; PSECURITY_DESCRIPTOR_RELATIVE pSecurityDescriptor = NULL; ULONG ulSecDescRelLen = 0; if (pKeyResult->bHasSdInfo) goto cleanup; status = RegDbGetKeyAclByKeyId(ghCacheConnection, pKeyResult->qwId, &pKeyResult->qwSdId, &pSecurityDescriptor, &ulSecDescRelLen); BAIL_ON_NT_STATUS(status); status = RegSrvSetKeySecurityDescriptor_inlock(pKeyResult, pSecurityDescriptor, ulSecDescRelLen); BAIL_ON_NT_STATUS(status); cleanup: LWREG_SAFE_FREE_MEMORY(pSecurityDescriptor); return status; error: pKeyResult->bHasSdInfo = FALSE; goto cleanup; } NTSTATUS SqliteCacheKeySecurityDescriptor( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; BAIL_ON_NT_INVALID_POINTER(pKeyResult); LWREG_LOCK_RWMUTEX_EXCLUSIVE(bInLock, &pKeyResult->mutex); status = SqliteCacheKeySecurityDescriptor_inlock(pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_RWMUTEX(bInLock, &pKeyResult->mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheUpdateSubKeysInfo_inlock( IN DWORD dwOffSet, IN OUT PREG_KEY_CONTEXT pKeyResult, OUT size_t* psNumSubKeys ) { NTSTATUS status = STATUS_SUCCESS; size_t sNumSubKeys = 0; PREG_DB_KEY* ppRegEntries = NULL; int iCount = 0; size_t sSubKeyLen = 0; status = RegDbQueryInfoKey(ghCacheConnection, pKeyResult->pwszKeyName, pKeyResult->qwId, dwDefaultCacheSize, dwOffSet, &sNumSubKeys, &ppRegEntries); BAIL_ON_NT_STATUS(status); for (iCount = 0; iCount < (int)sNumSubKeys; iCount++) { if (ppRegEntries[iCount]->pwszKeyName) { sSubKeyLen = RtlWC16StringNumChars(ppRegEntries[iCount]->pwszKeyName); } if (pKeyResult->sMaxSubKeyLen < sSubKeyLen) { pKeyResult->sMaxSubKeyLen = sSubKeyLen; } sSubKeyLen = 0; } cleanup: *psNumSubKeys = sNumSubKeys; RegDbSafeFreeEntryKeyList(sNumSubKeys, &ppRegEntries); return status; error: goto cleanup; } NTSTATUS SqliteCacheUpdateSubKeysInfo( IN DWORD dwOffSet, IN OUT PREG_KEY_CONTEXT pKeyResult, OUT size_t* psNumSubKeys ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; BAIL_ON_NT_INVALID_POINTER(pKeyResult); LWREG_LOCK_RWMUTEX_EXCLUSIVE(bInLock, &pKeyResult->mutex); status = SqliteCacheUpdateSubKeysInfo_inlock(dwOffSet, pKeyResult, psNumSubKeys); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_RWMUTEX(bInLock, &pKeyResult->mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheKeyValuesInfo_inlock( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; size_t sNumValues = 0; size_t sNumCacheValues = 0; PREG_DB_VALUE* ppRegEntries = NULL; if (pKeyResult->bHasValueInfo) { goto cleanup; } status = RegDbQueryInfoKeyCount(ghCacheConnection, pKeyResult->qwId, QueryValues, &sNumValues); BAIL_ON_NT_STATUS(status); sNumCacheValues = (sNumValues > (size_t)dwDefaultCacheSize) ? dwDefaultCacheSize : sNumValues; status = RegDbQueryInfoKeyValue(ghCacheConnection, pKeyResult->qwId, sNumCacheValues, 0, &sNumCacheValues, &ppRegEntries); BAIL_ON_NT_STATUS(status); status = RegDbSafeRecordValuesInfo_inlock( sNumValues, sNumCacheValues, ppRegEntries, pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: RegDbSafeFreeEntryValueList(sNumCacheValues, &ppRegEntries); return status; error: goto cleanup; } NTSTATUS SqliteCacheKeyValuesInfo( IN OUT PREG_KEY_CONTEXT pKeyResult ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; BAIL_ON_NT_INVALID_POINTER(pKeyResult); LWREG_LOCK_RWMUTEX_EXCLUSIVE(bInLock, &pKeyResult->mutex); status = SqliteCacheKeyValuesInfo_inlock(pKeyResult); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_RWMUTEX(bInLock, &pKeyResult->mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheUpdateValuesInfo_inlock( DWORD dwOffSet, IN OUT PREG_KEY_CONTEXT pKeyResult, OUT size_t* psNumValues ) { NTSTATUS status = STATUS_SUCCESS; size_t sNumValues = 0; PREG_DB_VALUE* ppRegEntries = NULL; int iCount = 0; size_t sValueNameLen = 0; DWORD dwValueLen = 0; status = RegDbQueryInfoKeyValue(ghCacheConnection, pKeyResult->qwId, dwDefaultCacheSize, dwOffSet, &sNumValues, &ppRegEntries); BAIL_ON_NT_STATUS(status); for (iCount = 0; iCount < (int)sNumValues; iCount++) { if (ppRegEntries[iCount]->pwszValueName) { sValueNameLen = RtlWC16StringNumChars(ppRegEntries[iCount]->pwszValueName); } if (pKeyResult->sMaxValueNameLen < sValueNameLen) pKeyResult->sMaxValueNameLen = sValueNameLen; status = RegCopyValueBytes(ppRegEntries[iCount]->pValue, ppRegEntries[iCount]->dwValueLen, NULL, &dwValueLen); BAIL_ON_NT_STATUS(status); if (pKeyResult->sMaxValueLen < (size_t)dwValueLen) pKeyResult->sMaxValueLen = (size_t)dwValueLen; sValueNameLen = 0; dwValueLen = 0; } cleanup: *psNumValues = sNumValues; RegDbSafeFreeEntryValueList(sNumValues, &ppRegEntries); return status; error: goto cleanup; } NTSTATUS SqliteCacheUpdateValuesInfo( DWORD dwOffSet, IN OUT PREG_KEY_CONTEXT pKeyResult, OUT size_t* psNumValues ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; BAIL_ON_NT_INVALID_POINTER(pKeyResult); LWREG_LOCK_RWMUTEX_EXCLUSIVE(bInLock, &pKeyResult->mutex); status = SqliteCacheUpdateValuesInfo_inlock(dwOffSet, pKeyResult, psNumValues); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_RWMUTEX(bInLock, &pKeyResult->mutex); return status; error: goto cleanup; } // Sqlite DB Key Index and ACL Index mapping cache void SqliteCacheFreeDbKeyHashEntry( IN const REG_HASH_ENTRY* pEntry ) { PREG_DB_KEY pRegKey = (PREG_DB_KEY)pEntry->pValue; if (pRegKey) { RegDbSafeFreeEntryKey(&pRegKey); } } NTSTATUS SqliteCacheGetDbKeyInfo( IN PCWSTR pwszKeyName, OUT PREG_DB_KEY* ppRegKey ) { NTSTATUS status = 0; BOOLEAN bInLock = FALSE; //Do not free PREG_DB_KEY pRegKeyRef = NULL; PREG_DB_KEY pRegKey = NULL; LWREG_LOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); status = RegHashGetValue(gRegDbKeyList.pKeyList, pwszKeyName, (PVOID*)&pRegKeyRef); BAIL_ON_NT_STATUS(status); status = RegDbDuplicateDbKeyEntry(pRegKeyRef, &pRegKey); BAIL_ON_NT_STATUS(status); *ppRegKey = pRegKey; cleanup: LWREG_UNLOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); return status; error: *ppRegKey = NULL; RegDbSafeFreeEntryKey(&pRegKey); goto cleanup; } NTSTATUS SqliteCacheInsertDbKeyInfo( IN PREG_DB_KEY pRegKey ) { NTSTATUS status = 0; BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); status = SqliteCacheInsertDbKeyInfo_inlock(pRegKey); BAIL_ON_NT_STATUS(status); cleanup: LWREG_UNLOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); return status; error: goto cleanup; } NTSTATUS SqliteCacheInsertDbKeyInfo_inlock( IN PREG_DB_KEY pRegKey ) { NTSTATUS status = STATUS_SUCCESS; BAIL_ON_NT_INVALID_POINTER(pRegKey); status = RegHashSetValue(gRegDbKeyList.pKeyList, (PVOID)pRegKey->pwszFullKeyName, (PVOID)pRegKey); BAIL_ON_NT_STATUS(status); cleanup: return status; error: goto cleanup; } VOID SqliteCacheDeleteDbKeyInfo( IN PCWSTR pwszKeyName ) { BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); SqliteCacheDeleteDbKeyInfo_inlock(pwszKeyName); LWREG_UNLOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); return; } VOID SqliteCacheDeleteDbKeyInfo_inlock( IN PCWSTR pwszKeyName ) { NTSTATUS status = 0; REG_HASH_ITERATOR hashIterator; REG_HASH_ENTRY* pHashEntry = NULL; PREG_DB_KEY pRegKey = NULL; int iCount = 0; status = RegHashGetValue(gRegDbKeyList.pKeyList, (PCVOID)pwszKeyName, (PVOID*)&pRegKey); if (STATUS_OBJECT_NAME_NOT_FOUND == status) { return; } RegHashGetIterator(gRegDbKeyList.pKeyList, &hashIterator); for (iCount = 0; (pHashEntry = RegHashNext(&hashIterator)) != NULL; iCount++) { if (LwRtlWC16StringIsEqual((PCWSTR)pHashEntry->pKey, pwszKeyName, FALSE)) { RegHashRemoveKey(gRegDbKeyList.pKeyList, pHashEntry->pKey); break; } } return; } VOID SqliteReleaseDbKeyInfo( PREG_DB_KEY pRegKey ) { BOOLEAN bInLock = FALSE; LWREG_LOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); SqliteReleaseDbKeyInfo_inlock(pRegKey); LWREG_UNLOCK_MUTEX(bInLock, &gRegDbKeyList.mutex); } VOID SqliteReleaseDbKeyInfo_inlock( PREG_DB_KEY pRegKey ) { if (pRegKey) { SqliteCacheDeleteDbKeyInfo_inlock(pRegKey->pwszFullKeyName); } }