/*++ Copyright (c) 1989-1997 Microsoft Corporation Module Name: table.c Abstract: This module contains the routines to manipulate the property id table in a property set. --*/ #include // needs propset.h and ntfsprop.h #define Dbg DEBUG_TRACE_PROP_FSCTL ULONG BinarySearchIdTable ( IN PPROPERTY_CONTEXT Context, IN PROPID PropertyId ) /*++ Routine Description: This routines performs a binary search on the Id table Arguments: Context - property context of table PropertyId - PROPID to find Return Value: Entry index where the specified property Id exists or where it should be inserted --*/ { int Hi, Lo; // // Set up for binary search. Entries between 0 and count-1 are eligible // Lo = 0; Hi = (int) Context->IdTable->PropertyCount - 1; // // While we have a valid range to search // while (Lo <= Hi) { int Mid; // // Determine midpoint where we'll test // Mid = (Lo + Hi) / 2; // // If we've found the item then return it // if (PropertyId == Context->IdTable->Entry[Mid].PropertyId) { PROPASSERT( Mid >= 0); return (ULONG) Mid; // // If the property is in the upper range then move // the lower boundary upward // } else if (PropertyId > Context->IdTable->Entry[Mid].PropertyId) { Lo = Mid + 1; // // The property is in the lower range. Move the upper boundary // downward // } else { Hi = Mid - 1; } } // // No exact match was found. Lo is the point before where the property Id // must be inserted. // PROPASSERT( Lo >= 0 && Lo <= (int) Context->IdTable->PropertyCount ); return (ULONG) Lo; } ULONG FindIdInTable ( IN PPROPERTY_CONTEXT Context, IN PROPID PropertyId ) /*++ Routine Description: This routines looks up a property by Id in the property set heap. Arguments: Context - property context of table PropertyId - PROPID to find Return Value: Offset to property value in heap. 0 if not found. --*/ { ULONG Index; DebugTrace( +1, Dbg, ("FindInTable(%x)\n", PropertyId) ); // // Binary search table for Id // Index = BinarySearchIdTable( Context, PropertyId ); // // If found entry is legal and it matches the Id then return // pointer to value header // if (Index < Context->IdTable->PropertyCount && Context->IdTable->Entry[Index].PropertyId == PropertyId) { DebugTrace( -1, Dbg, ("FindIdInTable: return offset %x (found)\n", Context->IdTable->Entry[Index].PropertyValueOffset )); return Context->IdTable->Entry[Index].PropertyValueOffset; } // // entry not found, return // DebugTrace( -1, Dbg, ("FindIdInTable: not found\n") ); return 0; } PROPID FindFreeIdInTable ( IN PPROPERTY_CONTEXT Context, IN PROPID Id ) /*++ Routine Description: This routines looks in the table to find the next propid beginning at Id that is not allocated Arguments: Context - property context of table Id - PROPID to being free searc at Return Value: PROPID of free Id --*/ { ULONG Index; // // Find location in table to begin search // Index = BinarySearchIdTable( Context, Id ); // // while location is valid and Id is the same // while (Index < Context->IdTable->PropertyCount && Context->IdTable->Entry[Index].PropertyId == Id) { // // advance location and Id // Index ++; Id ++; } return Id; } // // Local support routine // VOID GrowIdTable ( IN PPROPERTY_CONTEXT Context ) /*++ Routine Description: This routine grows the Id table in the property set. It handles resizing the attribute and moving the property heap. This means resetting the cached pointers inside of Context Arguments: Context - property context for this action. Return Value: Nothing. --*/ { ULONG Count = Context->IdTable->PropertyCount; ULONG Offset = Context->Header->ValueHeapOffset; // // We grow the property heap by max (PIT_PROPERTY_DELTA, Count / 16) // entries // ULONG Delta = max( PIT_PROPERTY_DELTA, Count / 16 ); ULONG NewSize = (ULONG) NtOfsQueryLength( Context->Attribute ) + Delta * sizeof( PROPERTY_TABLE_ENTRY ); DebugTrace( +1, Dbg, ("GrowIdTable growing by %x\n", Delta) ); // // Check for growing attribute too much. // BUGBUG - define a better status code. // if (NewSize > VACB_MAPPING_GRANULARITY) { ExRaiseStatus( STATUS_DISK_FULL ); } // // Resize the attribute // DebugTrace( 0, Dbg, ("Setting size to %x\n", Context->Attribute->Header.FileSize.QuadPart + Delta * sizeof( PROPERTY_TABLE_ENTRY )) ); NtOfsSetLength( Context->IrpContext, Context->Attribute, Context->Attribute->Header.FileSize.QuadPart + Delta * sizeof( PROPERTY_TABLE_ENTRY ) ); // // Move the heap upwards by Delta // DebugTrace( 0, Dbg, ("Moving heap from %x to %x\n", ContextOffset( Context, Context->HeapHeader ), ContextOffset( Context, Context->HeapHeader ) + Delta * sizeof( PROPERTY_TABLE_ENTRY )) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, Context->HeapHeader ) + Delta * sizeof( PROPERTY_TABLE_ENTRY ), Context->HeapHeader->PropertyHeapLength, Context->HeapHeader ); Offset += Delta * sizeof( PROPERTY_TABLE_ENTRY ); DebugTrace( 0, Dbg, ("Setting ValueHeapOffset to %x\n", Offset) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->Header->ValueHeapOffset ), sizeof( ULONG ), &Offset ); Context->HeapHeader = PROPERTY_HEAP_HEADER( Context->Header ); // // Update the max size by the delta // Count = Context->IdTable->MaximumPropertyCount + Delta; DebugTrace( 0, Dbg, ("Setting max table count to %x\n", Count) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->MaximumPropertyCount ), sizeof( ULONG ), &Count ); // // Rebase the entry pointers in the propinfo if any // if (Context->Info != NULL) { ULONG i; for (i = 0; i < Context->Info->Count; i++) { if (Context->Info->Entries[i].Heap != EMPTY_PROPERTY) { Context->Info->Entries[i].Heap = Add2Ptr( Context->Info->Entries[i].Heap, Delta ); } } } DebugTrace( -1, Dbg, ("") ); } VOID ChangeTable ( IN PPROPERTY_CONTEXT Context, IN PROPID Id, IN ULONG Offset ) /*++ Routine Description: This routine changes the table based on the new offset Arguments: Context - property context for this action. Id - PROPID to find Offset - New property value offset Return Value: Nothing. --*/ { ULONG Count = Context->IdTable->PropertyCount; // // Binary search table for Id // ULONG Index = BinarySearchIdTable( Context, Id ); // // If the offset is zero, then we are deleting the entry // if (Offset == 0) { // // Make sure the returned value makes sense // ASSERT ( Index < Count && Context->IdTable->Entry[Index].PropertyId == Id ); // // We move all entries Index+1..PropertyCount down to Index. Special case // moving the last item. // if (Index != Count - 1) { DebugTrace( 0, Dbg, ("Ripple copy %x to %x length %x\n", ContextOffset( Context, &Context->IdTable->Entry[Index + 1] ), ContextOffset( Context, &Context->IdTable->Entry[Index] ), sizeof( PROPERTY_TABLE_ENTRY ) * (Count - (Index + 1))) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->Entry[Index] ), sizeof( PROPERTY_TABLE_ENTRY ) * (Count - (Index + 1)), &Context->IdTable->Entry[Index + 1] ); } // // Change the count in use // Count--; DebugTrace( 0, Dbg, ("New count is %x\n", Count) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->PropertyCount ), sizeof( ULONG ), &Count ); } else { // // if we found the propertyid in the table // if (Index < Count && Context->IdTable->Entry[Index].PropertyId == Id) { PROPERTY_TABLE_ENTRY Entry = { Id, Offset }; // // Replace the entry in the table with the new entry // LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->Entry[Index] ), sizeof( PROPERTY_TABLE_ENTRY ), &Entry ); } else { PROPERTY_TABLE_ENTRY Entry = { Id, Offset }; // // Add the new entry to the table // // // If there is no more room in the table for a new Id then // grow the IdTable // if (Count == Context->IdTable->MaximumPropertyCount) { GrowIdTable( Context ); } // // Index is the point where the insertion must occur. We leave // alone elements 0..Index-1, and ripple-copy elements Index..PropertyCount-1 // to Index+1. We skip this case when we are simply appending a propid at the // end. // if (Index < Count) { DebugTrace( 0, Dbg, ("Ripple copy table from %x to %x length %x\n", PtrOffset( Context->IdTable, &Context->IdTable->Entry[Index] ), PtrOffset( Context->IdTable, &Context->IdTable->Entry[Index + 1]), sizeof( PROPERTY_TABLE_ENTRY ) * (Count - Index)) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->Entry[Index + 1] ), sizeof( PROPERTY_TABLE_ENTRY ) * (Count - Index), &Context->IdTable->Entry[Index] ); } // // Stick in the new property entry // DebugTrace( 0, Dbg, ("new entry %x:%x\n", Entry.PropertyId, Entry.PropertyValueOffset) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->Entry[Index] ), sizeof( PROPERTY_TABLE_ENTRY ), &Entry ); // // Increment the usage count and log it // Count++; DebugTrace( 0, Dbg, ("new count in table is %x\n", Count) ); LogFileFullFailCheck( Context->IrpContext ); NtOfsPutData( Context->IrpContext, Context->Attribute, ContextOffset( Context, &Context->IdTable->PropertyCount ), sizeof( ULONG ), &Count ); } } }