/*++ Copyright (c) 2001 Microsoft Corporation Module Name: sample.cxx Abstract: This file implements a sample peristance provider. This sample does not address buffer overflows, puts large buffers on the stack, etc. Caveat Emptor. Author: Cliff Van Dyke (cliffv) 9-May-2001 --*/ #include "pch.hxx" #include #include // // Local Storage // // Each provider is given a single PVOID on the AZP_ADMIN_MANAGER structure. // That PVOID is a pointer to whatever context the provider needs to maintain a // description of the local storage. // // The structure below is that context for the sample provider. // typedef struct _AZP_SAMPLE_CONTEXT { // // Handle to the file // HANDLE FileHandle; // // The next GUID to assign to an object // ??? A real provider would allocate a real GUID // ULONG LastUsedGuid; } AZP_SAMPLE_CONTEXT, *PAZP_SAMPLE_CONTEXT; // // Table of object type to text string mappings // LPWSTR ObjectTypeNames[] = { L"[ROOT]", L"[ADMIN_MANAGER]", L"[APPLICATION]", L"[OPERATION]", L"[TASK]", L"[SCOPE]", L"[GROUP]", L"[ROLE]", L"[JUNCTION_POINT]", L"[SID]" }; #define ObjectTypeNamesSize (sizeof(ObjectTypeNames)/sizeof(ObjectTypeNames[0])) // // Delimiter between names // #define SAMPLE_DELIM L"-->" #define SAMPLE_DELIM_SIZE (sizeof(SAMPLE_DELIM)-sizeof(WCHAR)) // // Define a buffer large enough for an object name // #define HUGE_BUFFER_SIZE (70000*sizeof(WCHAR)) DWORD SampleReadFile( IN LPWSTR FileName, IN BOOL CreatePolicy, OUT HANDLE *RetFileHandle, OUT LPBYTE *RetBuffer, OUT PULONG RetBufferSize ) /*++ Routine Description: This routine read the policy file into a buffer. Arguments: FileName - Name of the file containing the policy CreatePolicy - TRUE if the policy database is to be created. FALSE if the policy database already exists RetFileHandle - Returns a handle to the open file. The caller should close this file by calling CloseHandle. RetBuffer - Returns a pointer to a buffer containing the contents of the file The caller should free this buffer by calling AzpFreeHeap. RetBufferSize - Size (in bytes) of RetBuffer Return Value: Status of the operation --*/ { DWORD WinStatus; HANDLE FileHandle; LPBYTE Buffer = NULL; ULONG BufferSize = 0;; ULONG BytesRead; // // Open the file // *RetFileHandle = INVALID_HANDLE_VALUE; *RetBuffer = NULL; *RetBufferSize = 0; FileHandle = CreateFileW( FileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CreatePolicy ? CREATE_NEW : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( FileHandle == INVALID_HANDLE_VALUE ) { WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SampleReadFile: Cannot CreateFile %ld\n", WinStatus )); goto Cleanup; } // // Allocate a buffer to read the file into // BufferSize = GetFileSize( FileHandle, NULL ); if ( BufferSize == 0xFFFFFFFF ) { WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SampleReadFile: Cannot GetFileSize %ld\n", WinStatus )); goto Cleanup; } Buffer = (LPBYTE) AzpAllocateHeap( BufferSize + sizeof(WCHAR) ); if ( Buffer == NULL ) { WinStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } if ( BufferSize == 0 ) { *(LPWSTR)Buffer = '\0'; WinStatus = NO_ERROR; goto Cleanup; } // // Read the file into the buffer // if ( !ReadFile( FileHandle, Buffer, BufferSize, &BytesRead, NULL ) ) { // Not Overlapped WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SampleReadFile: Cannot ReadFile %ld\n", WinStatus )); goto Cleanup; } if ( BytesRead != BufferSize ) { WinStatus = ERROR_INTERNAL_DB_CORRUPTION; AzPrint(( AZD_PERSIST, "SampleReadFile: Cannot ReadFile right size %ld\n", WinStatus )); goto Cleanup; } WinStatus = NO_ERROR; // // Return the information to the caller // Cleanup: if ( WinStatus == NO_ERROR ) { *RetFileHandle = FileHandle; *RetBuffer = Buffer; Buffer = NULL; *RetBufferSize = BufferSize; } // // Free locally used resources // if ( Buffer != NULL ) { AzpFreeHeap( Buffer ); } return WinStatus; } BOOL SampleGetALine( IN OUT LPWSTR * LinePointer, OUT PULONG RetLevel, OUT PULONG RetObjectType, OUT PULONG Guid, OUT LPWSTR FullNameBuffer, OUT LPWSTR *Name ) /*++ Routine Description: This routine gets the next line from a buffer containing the policy file. Arguments: LinePointer - On input, points to the address of the line to read. On output, points to the next line. RetLevel - Returns the parent/child "generation" level of the current line. 0 is AdminManager itself. Children of AdminManager are 1. etc. RetObjectType - Returns the object type of the line Guid - Returns the GUID of the object FullNameBuffer - Returns the full parent/child name of the object. (e.g. AppName->ScopeName->RoleName) Pass in a huge buffer here. Name - Returns a pointer into FullNameBuffer of the last component name. (e.g., a pointer to RoleName in the sample above) Return Value: TRUE: Line was valid FALSE: EOF --*/ { BOOL RetVal; LPWSTR Line = *LinePointer; LPWSTR Next = NULL; WCHAR *p; LONG Level = 0; ULONG ObjectTypeLength; LPWSTR ObjectTypeString; ULONG ObjectType = 0; ULONG ObjectNameLength; LPWSTR ObjectNameString; // // Get a pointer to the next line of the file // Next = wcschr( Line, '\n' ); if ( Next == NULL ) { RetVal = FALSE; goto Cleanup; } Next ++; p = Line; // // Parse off the "Guid" // *Guid = wcstoul( p, &p, 10 ); if ( *Guid == 0 ) { AzPrint(( AZD_PERSIST, "SampleGetALine: No Guid on line %ws\n", Line )); RetVal = FALSE; goto Cleanup; } // Skip over the space p++; // // Parse off the object name // ObjectNameString = p; ObjectNameLength = wcscspn( p, L"[\n" ); if ( ObjectNameLength == 0 || ObjectNameLength == 1 ) { AzPrint(( AZD_PERSIST, "SampleGetALine: No object name on line %ws\n", Line )); RetVal = FALSE; goto Cleanup; } ObjectNameLength--; if ( ObjectNameString[ObjectNameLength] != ' ' ) { AzPrint(( AZD_PERSIST, "SampleGetALine: object name not blank terminated on line %ws\n", Line )); RetVal = FALSE; goto Cleanup; } RtlCopyMemory( FullNameBuffer, ObjectNameString, ObjectNameLength*sizeof(WCHAR) ); FullNameBuffer[ObjectNameLength] = '\0'; p = &p[ObjectNameLength+1]; // // Parse off the object type string // ObjectTypeString = p; ObjectTypeLength = wcscspn( p, L" \n" ); if ( ObjectTypeLength == 0 ) { AzPrint(( AZD_PERSIST, "SampleGetALine: No object type on line %ws\n", Line )); RetVal = FALSE; goto Cleanup; } p = &p[ObjectTypeLength+1]; // // Convert the string to a number // for ( ObjectType = 0; ObjectType < ObjectTypeNamesSize; ObjectType++ ) { if ( wcsncmp( ObjectTypeString, ObjectTypeNames[ObjectType], ObjectTypeLength ) == 0 ) { break; } } if ( ObjectType >= ObjectTypeNamesSize ) { AzPrint(( AZD_PERSIST, "SampleGetALine: Bad object type %ws\n", Line )); RetVal = FALSE; goto Cleanup; } // // Determine the level of this entry // Level = 1; // AdminManager is level 0 p = FullNameBuffer; *Name = p; for (;;) { p = wcsstr(p, SAMPLE_DELIM ); if ( p == NULL ) { break; } p += SAMPLE_DELIM_SIZE/sizeof(WCHAR); Level ++; *Name = p; } RetVal = TRUE; // // Free locally used resources // Cleanup: *RetObjectType = ObjectType; *RetLevel = Level; *LinePointer = Next; return RetVal; } DWORD SamplePersistOpen( IN PAZP_ADMIN_MANAGER AdminManager, IN BOOL CreatePolicy ) /*++ Routine Description: This routine submits reads the authz policy database from storage. This routine also reads the policy database into cache. On Success, the caller should call SamplePersistClose to free any resources consumed by the open. On entry, AzGlResource must be locked exclusive. Arguments: AdminManager - Specifies the policy database that is to be read. CreatePolicy - TRUE if the policy database is to be created. FALSE if the policy database already exists Return Value: NO_ERROR - The operation was successful ERROR_NOT_ENOUGH_MEMORY - not enough memory Other status codes --*/ { DWORD WinStatus; PAZP_SAMPLE_CONTEXT PersistContext = NULL; LPWSTR Line; LPWSTR Next; LPBYTE Buffer = NULL; ULONG BufferSize; ULONG Level; ULONG CurrentLevel = 0; PGENERIC_OBJECT GenericObjectStack[8]; LPWSTR BigBuffer = NULL; // // Initialization // ASSERT( AzpIsLockedExclusive( &AzGlResource ) ); RtlZeroMemory( &GenericObjectStack[0], sizeof(GenericObjectStack) ); GenericObjectStack[0] = (PGENERIC_OBJECT) AdminManager; SafeAllocaAllocate( BigBuffer, HUGE_BUFFER_SIZE ); if ( BigBuffer == NULL ) { WinStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // Allocate a context describing this provider // PersistContext = (PAZP_SAMPLE_CONTEXT) AzpAllocateHeap( sizeof(*PersistContext) ); if ( PersistContext == NULL ) { WinStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } PersistContext->LastUsedGuid = 0; // // Read the file into a buffer // WinStatus = SampleReadFile( AdminManager->PolicyUrl.String, CreatePolicy, &PersistContext->FileHandle, &Buffer, &BufferSize ); if ( WinStatus != NO_ERROR ) { AzPrint(( AZD_PERSIST, "SamplePersistOpen: Cannot SampleReadFile %ld\n", WinStatus )); goto Cleanup; } // // Loop through the file a line at a time // for ( Line = (LPWSTR)Buffer; Line != NULL ; Line = Next ) { ULONG ObjectType; LPWSTR ObjectNameString; AZP_STRING ObjectName; ULONG ObjectGuid; PGENERIC_OBJECT_HEAD ChildGenericObjectHead; // // Grab a line from the file // Next = Line; if ( !SampleGetALine( &Next, &Level, &ObjectType, &ObjectGuid, BigBuffer, &ObjectNameString ) ) { break; } // // Cannot be more than one greater than the previous level // ASSERT( Level >= 1 ); if ( Level > CurrentLevel+1 ) { AzPrint(( AZD_PERSIST, "SamplePersistOpen: %ld %ld File broken on line %ws\n", Level, CurrentLevel, Line )); WinStatus = ERROR_INTERNAL_DB_CORRUPTION; goto Cleanup; } // // Close any stacked pointers we'll no longer use // while ( CurrentLevel >= Level ) { ObDereferenceObject( GenericObjectStack[CurrentLevel] ); GenericObjectStack[CurrentLevel] = NULL; CurrentLevel --; } CurrentLevel = Level; AzPrint(( AZD_PERSIST_MORE, "Open: %ws\n", ObjectNameString )); AzpInitString( &ObjectName, ObjectNameString ); // // Search for the list to link this item into // for ( ChildGenericObjectHead = GenericObjectStack[CurrentLevel-1]->ChildGenericObjectHead; ChildGenericObjectHead != NULL; ChildGenericObjectHead = ChildGenericObjectHead->SiblingGenericObjectHead ) { if ( ObjectType == ChildGenericObjectHead->ObjectType ) { break; } } if ( ChildGenericObjectHead == NULL ) { AzPrint(( AZD_PERSIST, "SamplePersistOpen: Parent doesn't support this child %ld %ws\n", ObjectType, ObjectNameString )); WinStatus = ERROR_INTERNAL_DB_CORRUPTION; goto Cleanup; } // // Actually create the object in the cache // WinStatus = ObCreateObject( GenericObjectStack[CurrentLevel-1], // ParentGenericObject ChildGenericObjectHead, ObjectType, &ObjectName, &GenericObjectStack[CurrentLevel] ); // Save pointer to generic object if ( ObjectType >= ObjectTypeNamesSize ) { AzPrint(( AZD_PERSIST, "SamplePersistOpen: Cannot CreateObject from DB %ld %ld %ws\n", WinStatus, ObjectType, ObjectNameString )); goto Cleanup; } // // Remember the GUID of the object // *(PULONG)&GenericObjectStack[CurrentLevel]->PersistenceGuid = ObjectGuid; PersistContext->LastUsedGuid = ObjectGuid; // // ??? A Real provider would set the attributes on the object here // } // // Return the context to the caller // AdminManager->PersistContext = PersistContext; PersistContext = NULL; WinStatus = NO_ERROR; // // Free locally used resources // Cleanup: if ( PersistContext != NULL ) { AdminManager->PersistContext = PersistContext; SamplePersistClose( AdminManager ); } while ( CurrentLevel >= 1 ) { ObDereferenceObject( GenericObjectStack[CurrentLevel] ); GenericObjectStack[CurrentLevel] = NULL; CurrentLevel --; } if ( Buffer != NULL ) { AzpFreeHeap( Buffer ); } SafeAllocaFree( BigBuffer ); return WinStatus; } VOID SamplePersistClose( IN PAZP_ADMIN_MANAGER AdminManager ) /*++ Routine Description: This routine submits close the authz policy database storage handles. On entry, AzGlResource must be locked exclusive. Arguments: AdminManager - Specifies the policy database that is to be read. Return Value: NO_ERROR - The operation was successful ERROR_NOT_ENOUGH_MEMORY - not enough memory Other status codes --*/ { PAZP_SAMPLE_CONTEXT PersistContext = (PAZP_SAMPLE_CONTEXT) AdminManager->PersistContext; ASSERT(PersistContext != NULL); ASSERT( AzpIsLockedExclusive( &AzGlResource ) ); // // Close the file handle // CloseHandle( PersistContext->FileHandle ); // // Free the context itself // AzpFreeHeap( PersistContext ); AdminManager->PersistContext = NULL; } DWORD SamplePersistSubmit( IN PGENERIC_OBJECT GenericObject, IN BOOLEAN DeleteMe ) /*++ Routine Description: This routine submits changes made to the authz policy database. If the object is being created, the GenericObject->PersistenceGuid field will be zero on input. Upon successful creation, this routine will set PersistenceGuid to non-zero. Upon failed creation, this routine will leave PersistenceGuid as zero. On entry, AzGlResource must be locked exclusive. Arguments: GenericObject - Specifies the object in the database that is to be updated in the underlying store. DeleteMe - TRUE if the object and all of its children are to be deleted. FALSE if the object is to be updated. Return Value: NO_ERROR - The operation was successful ERROR_NOT_ENOUGH_MEMORY - not enough memory Other status codes --*/ { DWORD WinStatus; PAZP_SAMPLE_CONTEXT PersistContext = (PAZP_SAMPLE_CONTEXT) GenericObject->AdminManagerObject->PersistContext; HANDLE FileHandle = INVALID_HANDLE_VALUE; BOOLEAN AllocatedGuid = FALSE; // Variables describing the GenericObject entry LPWSTR NewEntryBuffer = NULL; ULONG NewEntryLevel; ULONG NewEntryNameByteCount; AZP_STRING NewEntryObjectName; ULONG NewGuid; BOOL NewInserted = FALSE; PGENERIC_OBJECT CurrentGenericObject; PGENERIC_OBJECT NextGenericObject; ULONG BytesWritten; LPBYTE Where; LPWSTR Line; LPWSTR Next; LPWSTR CurrentFullObjectName = NULL; // Variables describing the existing file contents LPBYTE Buffer = NULL; ULONG BufferSize; // // Stack of descriptors of the buffers to write to the file // ULONG Index; ULONG StackIndex; LPBYTE BufferStack[30]; ULONG SizeStack[30]; LPSTR CommentStack[30]; // // Initialization // ASSERT( AzpIsLockedExclusive( &AzGlResource ) ); SafeAllocaAllocate( CurrentFullObjectName, HUGE_BUFFER_SIZE ); if ( CurrentFullObjectName == NULL ) { WinStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } // // If the object doesn't yet have a GUID, // give it one. // if ( (*(PULONG)(&GenericObject->PersistenceGuid)) == 0 ) { PersistContext->LastUsedGuid ++; *(PULONG)(&GenericObject->PersistenceGuid) = PersistContext->LastUsedGuid; AllocatedGuid = TRUE; } NewGuid = *(PULONG)(&GenericObject->PersistenceGuid); // // Build the bytes to write to the file for the entry representing GenericObject // // SafeAllocaAllocate( NewEntryBuffer, HUGE_BUFFER_SIZE ); if ( NewEntryBuffer == NULL ) { WinStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } NewEntryBuffer[0] = 0; NewEntryLevel = 0; // // Build the "GUID" first // swprintf( NewEntryBuffer, L"%ld ", *(PULONG)(&GenericObject->PersistenceGuid) ); // // Build the object name next // Where = ((LPBYTE)NewEntryBuffer) + HUGE_BUFFER_SIZE - sizeof(WCHAR); NewEntryNameByteCount = sizeof(WCHAR); *((LPWSTR)Where) = '\0'; for ( CurrentGenericObject=GenericObject; CurrentGenericObject != NULL; CurrentGenericObject=NextGenericObject ) { // // Copy the delimiter into the buffer // if ( CurrentGenericObject != GenericObject ) { Where -= SAMPLE_DELIM_SIZE; NewEntryNameByteCount += SAMPLE_DELIM_SIZE; RtlCopyMemory( Where, SAMPLE_DELIM, SAMPLE_DELIM_SIZE ); } // // Copy the name of the current object into the buffer // Where -= CurrentGenericObject->ObjectName.StringSize-sizeof(WCHAR); NewEntryNameByteCount += CurrentGenericObject->ObjectName.StringSize-sizeof(WCHAR); RtlCopyMemory( Where, CurrentGenericObject->ObjectName.String, CurrentGenericObject->ObjectName.StringSize-sizeof(WCHAR) ); // // Get the address of the current object // NextGenericObject = CurrentGenericObject->ParentGenericObjectHead->ParentGenericObject; if ( NextGenericObject->ObjectType == OBJECT_TYPE_ADMIN_MANAGER ) { break; } } // // Move the data to the right spot in the buffer // NewEntryObjectName.String = &NewEntryBuffer[ (wcslen(NewEntryBuffer) * sizeof(WCHAR)) / sizeof(WCHAR) ]; NewEntryObjectName.StringSize = NewEntryNameByteCount; RtlMoveMemory( NewEntryObjectName.String, Where, NewEntryNameByteCount ); // // Output the object type // wcscat( NewEntryBuffer, L" " ); if ( GenericObject->ObjectType < ObjectTypeNamesSize ) { wcscat( NewEntryBuffer, ObjectTypeNames[GenericObject->ObjectType] ); } else { wcscat( NewEntryBuffer, L"[unknown]" ); } // // Add modifiers // ??? A real provider actually should persist the attributes of the object // if ( GenericObject->Flags & GENOBJ_FLAGS_DELETED ) { wcscat( NewEntryBuffer, L" [DEL]" ); } if ( GenericObject->Flags & GENOBJ_FLAGS_DIRTY ) { wcscat( NewEntryBuffer, L" [DIRTY]" ); } wcscat( NewEntryBuffer, L"\n" ); AzPrint(( AZD_PERSIST_MORE, "%ws", NewEntryBuffer )); // // Read the file into a buffer // WinStatus = SampleReadFile( GenericObject->AdminManagerObject->PolicyUrl.String, FALSE, // Open existing file &FileHandle, &Buffer, &BufferSize ); if ( WinStatus != NO_ERROR ) { AzPrint(( AZD_PERSIST, "SamplePersistOpen: Cannot SampleReadFile %ld\n", WinStatus )); goto Cleanup; } // // Loop through the file a line at a time. // // Build a stack of the buffer fragments to write to the destination file. // Essentially, write the entire original buffer. // But, delete the object from the original buffer that has a guid matching the written object // And, insert the written object at the correct spot in the hierarchy. // // // The stack index points at the entry descrbing the 'remainder' of the buffer // StackIndex = 0; BufferStack[StackIndex] = Buffer; SizeStack[StackIndex] = BufferSize; CommentStack[StackIndex] = "First"; for ( Line = (LPWSTR)Buffer; Line != NULL ; Line = Next ) { ULONG CurrentObjectType; LPWSTR CurrentObjectNameString; AZP_STRING CurrentObjectName; ULONG CurrentLevel; ULONG CurrentGuid; LONG CompareResult; BOOL InsertHere; // // Grab a line from the file // Next = Line; if ( !SampleGetALine( &Next, &CurrentLevel, &CurrentObjectType, &CurrentGuid, CurrentFullObjectName, &CurrentObjectNameString ) ) { break; } AzpInitString( &CurrentObjectName, CurrentFullObjectName ); // // Delete the object that has the matching guid // if ( CurrentGuid == NewGuid ) { // // Truncate the current buffer remainder to stop right before the current line // SizeStack[StackIndex] = (ULONG)(((LPBYTE)Line) - BufferStack[StackIndex]); // // Push a new 'remainder' onto the stack // StackIndex++; BufferStack[StackIndex] = (LPBYTE) Next; SizeStack[StackIndex] = (ULONG)(BufferSize - (BufferStack[StackIndex] - Buffer)); CommentStack[StackIndex] = "Guid"; continue; } // // Determine if this is where we insert the object being written // InsertHere = FALSE; CompareResult = AzpCompareStrings( &CurrentObjectName, &NewEntryObjectName ); if ( CompareResult == 0 ) { WinStatus = GetLastError(); goto Cleanup; // // If the current line has the same name as the one being written, // inspect the types. // } else if ( CompareResult == CSTR_EQUAL ) { // // If the object types are the same, // replace the current object. // if ( CurrentObjectType == GenericObject->ObjectType ) { ASSERT( FALSE ); // The GUIDS didn't match AzPrint(( AZD_PERSIST_MORE, "equal equal '%ws' '%ws'\n", CurrentObjectName.String, NewEntryObjectName.String )); continue; // // if the current object type is greater than the one being written, // we've already passed it. // } else if ( CurrentObjectType > GenericObject->ObjectType ) { AzPrint(( AZD_PERSIST_MORE, "equal lt '%ws' '%ws'\n", CurrentObjectName.String, NewEntryObjectName.String )); InsertHere = TRUE; } else { } // // If the current line is greater than the one being written, // we've already passed it. We're done looping // } else if ( CompareResult == CSTR_GREATER_THAN ) { AzPrint(( AZD_PERSIST_MORE, "gt '%ws' '%ws'\n", CurrentObjectName.String, NewEntryObjectName.String )); InsertHere = TRUE; } // // Put the current object here // if ( InsertHere ) { if ( !DeleteMe ) { // // Truncate the current buffer remainder to stop right before the current line // SizeStack[StackIndex] = (ULONG)(((LPBYTE)Line) - BufferStack[StackIndex]); // // Push the new object onto the stack // StackIndex++; BufferStack[StackIndex] = (LPBYTE) NewEntryBuffer; SizeStack[StackIndex] = wcslen(NewEntryBuffer) * sizeof(WCHAR); CommentStack[StackIndex] = "New"; NewInserted = TRUE; // // Push a new 'remainder' onto the stack // StackIndex++; BufferStack[StackIndex] = (LPBYTE) Line; SizeStack[StackIndex] = (ULONG)(BufferSize - (BufferStack[StackIndex] - Buffer)); CommentStack[StackIndex] = "Post"; } break; } } // // If we didn't insert it yet, // do it now if ( !NewInserted && !DeleteMe ) { // // Push the new object onto the stack // StackIndex++; BufferStack[StackIndex] = (LPBYTE) NewEntryBuffer; SizeStack[StackIndex] = wcslen(NewEntryBuffer) * sizeof(WCHAR); CommentStack[StackIndex] = "New"; NewInserted = TRUE; } // // Truncate the file since we're writing it from scratch // ??? A real provider wouldn't write the whole database from scratch // if ( SetFilePointer( FileHandle, 0, NULL, FILE_BEGIN ) == INVALID_SET_FILE_POINTER ) { WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SamplePersistSubmit: Cannot SetFilePointer %ld\n", WinStatus )); goto Cleanup; } if ( !SetEndOfFile( FileHandle ) ) { WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SamplePersistSubmit: Cannot SetEndofFile %ld\n", WinStatus )); goto Cleanup; } // // Write the various parts of the data back to the file // // ??? A real provider should ensure that a failed write // doesn't destroy previous good written data. Either the write // of the entire database can be atomic. Or the write of this // particular object can be atomic. // for ( Index=0; Index<=StackIndex; Index++ ) { if ( BufferStack[Index] != NULL && SizeStack[Index] != 0 ) { AzPrint(( AZD_PERSIST_MORE, "%s: %ld %*.*ws\n", CommentStack[Index], SizeStack[Index], SizeStack[Index]/sizeof(WCHAR), SizeStack[Index]/sizeof(WCHAR), BufferStack[Index] )); if ( !WriteFile( FileHandle, BufferStack[Index], SizeStack[Index], &BytesWritten, NULL ) ) { WinStatus = GetLastError(); AzPrint(( AZD_PERSIST, "SamplePersistSubmit: Cannot WriteFile %ld\n", WinStatus )); goto Cleanup; } } } WinStatus = NO_ERROR; // // Free locally used resources // Cleanup: if ( WinStatus != NO_ERROR ) { if ( AllocatedGuid ) { RtlZeroMemory( &GenericObject->PersistenceGuid, sizeof(GenericObject->PersistenceGuid) ); } } if ( Buffer != NULL ) { AzpFreeHeap( Buffer ); } if ( FileHandle != INVALID_HANDLE_VALUE ) { CloseHandle( FileHandle ); } SafeAllocaFree( NewEntryBuffer ); SafeAllocaFree( CurrentFullObjectName ); return WinStatus; } DWORD SamplePersistRefresh( IN PGENERIC_OBJECT GenericObject ) /*++ Routine Description: This routine updates the attributes of the object from the policy database. On entry, AzGlResource must be locked exclusive. Arguments: GenericObject - Specifies the object in the database whose cache entry is to be updated The GenericObject->PersistenceGuid field should be non-zero on input. Return Value: NO_ERROR - The operation was successful ERROR_NOT_ENOUGH_MEMORY - not enough memory Other status codes --*/ { DWORD WinStatus = NO_ERROR; ASSERT( AzpIsLockedExclusive( &AzGlResource ) ); UNREFERENCED_PARAMETER( GenericObject ); // // ??? A real provider needs to implement this routine // return WinStatus; }