diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/views | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/cntfs/views')
-rw-r--r-- | private/ntos/cntfs/views/check.c | 282 | ||||
-rw-r--r-- | private/ntos/cntfs/views/driver.c | 287 | ||||
-rw-r--r-- | private/ntos/cntfs/views/heap.c | 506 | ||||
-rw-r--r-- | private/ntos/cntfs/views/initprop.c | 122 | ||||
-rw-r--r-- | private/ntos/cntfs/views/makefile | 13 | ||||
-rw-r--r-- | private/ntos/cntfs/views/readprop.c | 802 | ||||
-rw-r--r-- | private/ntos/cntfs/views/sources | 56 | ||||
-rw-r--r-- | private/ntos/cntfs/views/table.c | 543 | ||||
-rw-r--r-- | private/ntos/cntfs/views/viewprop.h | 600 | ||||
-rw-r--r-- | private/ntos/cntfs/views/views.reg | 7 | ||||
-rw-r--r-- | private/ntos/cntfs/views/writprop.c | 700 |
11 files changed, 3918 insertions, 0 deletions
diff --git a/private/ntos/cntfs/views/check.c b/private/ntos/cntfs/views/check.c new file mode 100644 index 000000000..c07b71acd --- /dev/null +++ b/private/ntos/cntfs/views/check.c @@ -0,0 +1,282 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + check.c + +Abstract: + + This module contains the property set check support + + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#define Dbg DEBUG_TRACE_PROP_FSCTL + + +VOID +CheckPropertySet ( + IN PPROPERTY_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine performs validates the syntax of the property set + stream. + +Arguments: + + Context - context of call + +Return Value: + + Nothing. May raise if object is corrupt. + +--*/ + +{ + PPROPERTY_SET_HEADER Header = Context->Header; + PPROPERTY_ID_TABLE IdTable = Context->IdTable; + PPROPERTY_HEAP_HEADER HeapHeader = Context->HeapHeader; + PPROPERTY_HEAP_ENTRY HeapEntry; + ULONG Length = (ULONG) Context->Attribute->Header.FileSize.QuadPart ; + ULONG i; + + if ( + // + // Verify initial length + // + + (Length < sizeof( PROPERTY_SET_HEADER ) + DebugDoit( && PROPASSERT( !"Not enough room for header" ))) || + + // + // Verify header of attribute. Check the signature and format stamp. + // + + (Header->wByteOrder != 0xFFFE + DebugDoit( && PROPASSERT( !"Byte order invalid" ))) || + (Header->wFormat != PSH_FORMAT_VERSION + DebugDoit( && PROPASSERT( !"Format version invalid" ))) || + ((HIWORD( Header->dwOSVer ) > 2 || + LOBYTE( LOWORD( Header->dwOSVer )) > 5) + DebugDoit( && PROPASSERT( !"dwOSVer invalid" ))) || + + // + // Verify offsets of table and heap are valid. + // + + (Header->IdTableOffset >= Length + DebugDoit( && PROPASSERT( !"IdTable offset invalid" ))) || + (Header->IdTableOffset != LongAlign( Header->IdTableOffset ) + DebugDoit( && PROPASSERT( !"IdTable misaligned" ))) || + (Header->ValueHeapOffset >= Length + DebugDoit( && PROPASSERT( !"ValueHeap offset invalid" ))) || + (Header->ValueHeapOffset != LongAlign( Header->ValueHeapOffset ) + DebugDoit( && PROPASSERT( !"ValueHeap misaligned" ))) || + + // + // Verify that the table fits below the value heap + // + + (PROPERTY_ID_TABLE_SIZE( IdTable->MaximumPropertyCount ) > + Header->ValueHeapOffset - Header->IdTableOffset + DebugDoit( && PROPASSERT( !"IdTable overlaps ValueHeap" ))) || + + // + // Verify that the heap is within the stream + // + + (Header->ValueHeapOffset + HeapHeader->PropertyHeapLength > Length + DebugDoit( && PROPASSERT( !"ValueHeap beyond end of stream" ))) || + + // + // Verify table counts are correct + // + + (IdTable->PropertyCount > IdTable->MaximumPropertyCount + DebugDoit( && PROPASSERT( !"IdTable counts are incorrect" ))) + + ) { + + NtfsRaiseStatus( Context->IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Context->Object ); + } + + for (i = 0; i < IdTable->PropertyCount; i++) { + PPROPERTY_TABLE_ENTRY Entry = (PPROPERTY_TABLE_ENTRY) &IdTable->Entry[i]; + if ( + // + // Verify sorting + // + + (i > 1 && Entry[-1].PropertyId >= Entry[0].PropertyId + DebugDoit( && PROPASSERT( !"IdTable entry sort invalid" ))) || + (i < IdTable->PropertyCount - 1 && Entry[0].PropertyId >= Entry[1].PropertyId + DebugDoit( && PROPASSERT( !"IdTable entry sort invalid 2" ))) || + + // + // Verify offset points within heap + // + + (Entry[0].PropertyValueOffset >= HeapHeader->PropertyHeapLength + DebugDoit( && PROPASSERT( !"IdTable entry offset invalid" ))) || + + // + // Verify the back pointer matches + // + + (Entry[0].PropertyId != GET_HEAP_ENTRY( HeapHeader, Entry[0].PropertyValueOffset )->PropertyId + DebugDoit( && PROPASSERT( !"Backpointer invalid" ))) + ) { + + NtfsRaiseStatus( Context->IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Context->Object ); + } + } + + HeapEntry = FIRST_HEAP_ENTRY( HeapHeader ); + while (!IS_LAST_HEAP_ENTRY( HeapHeader, HeapEntry )) { + + if (HeapEntry->PropertyId != PID_ILLEGAL) { + ULONG Index = + BinarySearchIdTable( Context, HeapEntry->PropertyId ); + + if ( + // + // Verify length is aligned + // + + (HeapEntry->PropertyValueLength != LongAlign( HeapEntry->PropertyValueLength ) + DebugDoit( && PROPASSERT( !"Property length misaligned" ))) || + // + // Verify backpointer works + // + + (Index >= IdTable->PropertyCount + DebugDoit( && PROPASSERT( !"Backpointer after end of table" ))) || + + // + // Backpointer Id agrees + // + + (IdTable->Entry[Index].PropertyId != HeapEntry->PropertyId + DebugDoit( && PROPASSERT( !"Backpointer not found in table" ))) || + + // + // Backpointer offset agrees + // + + (IdTable->Entry[Index].PropertyValueOffset != HeapOffset( Context, HeapEntry) + DebugDoit( && PROPASSERT( !"Backpointer not found in table" ))) || + + // + // Name length is word aligned + // + + (HeapEntry->PropertyNameLength != WordAlign( HeapEntry->PropertyNameLength ) + DebugDoit( && PROPASSERT( !"Name is odd number of bytes" ))) || + + // + // Verify property is entirely in heap + // + + (HeapOffset( Context, NEXT_HEAP_ENTRY( HeapEntry)) > HeapHeader->PropertyHeapLength + DebugDoit( && PROPASSERT( !"Property Value overlaps end of heap" ))) + + ) { + + NtfsRaiseStatus( Context->IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Context->Object ); + } + } + + HeapEntry = NEXT_HEAP_ENTRY( HeapEntry ); + } +} + + +VOID +DumpPropertyData ( + IN PPROPERTY_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine performs validates the syntax of the property set + stream. + +Arguments: + + Context - context of call + +Return Value: + + None + +--*/ + +{ + PPROPERTY_HEAP_ENTRY HeapEntry; + + ULONG i; + + // + // Map attribute for full size + // + + MapPropertyContext( Context ); + + // + // Verify the property set has valid contents. + // + + CheckPropertySet( Context ); + + // + // Dump out contents of property set + // + + DebugTrace( 0, Dbg, ("Property set dump\n") ); + DebugTrace( 0, Dbg, ("wByteOrder %04x wFormat %04x dwOSVer %08x\n", + Context->Header->wByteOrder, + Context->Header->wFormat, + Context->Header->dwOSVer) ); + DebugTrace( 0, Dbg, ("IdTableOffset %08x ValueHeapOffset %08x\n", + Context->Header->IdTableOffset, + Context->Header->ValueHeapOffset) ); + + DebugTrace( 0, Dbg, ("IdTable %x/%x entries used\n", + Context->IdTable->PropertyCount, + Context->IdTable->MaximumPropertyCount) ); + for (i = 0; i < Context->IdTable->PropertyCount; i++) { + DebugTrace( 0, Dbg, (" Entry[%d].PropertyId %08x .Header %08x\n", + i, + Context->IdTable->Entry[i].PropertyId, + Context->IdTable->Entry[i].PropertyValueOffset) ); + } + + + DebugTrace( 0, Dbg, ("PropertyHeapLength %08x\n", + Context->HeapHeader->PropertyHeapLength) ); + + HeapEntry = FIRST_HEAP_ENTRY( Context->HeapHeader ); + while (!IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) { + DebugTrace( 0, Dbg, (" Heap[%08x].Length %08x .PropertyId %08x .PropertyNameLength %04x\n", + HeapOffset( Context, HeapEntry ), + HeapEntry->PropertyValueLength, + HeapEntry->PropertyId, + HeapEntry->PropertyNameLength) ); + DebugTrace( 0, Dbg, (" .PropertyName '%.*ws'\n", + HeapEntry->PropertyNameLength / sizeof( WCHAR ), + HeapEntry->PropertyName) ); + HeapEntry = NEXT_HEAP_ENTRY( HeapEntry ); + } + +} + diff --git a/private/ntos/cntfs/views/driver.c b/private/ntos/cntfs/views/driver.c new file mode 100644 index 000000000..f1b24b06c --- /dev/null +++ b/private/ntos/cntfs/views/driver.c @@ -0,0 +1,287 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + driver.h + +Abstract: + + This module contains the user FsCtls for the Ntfs Property support. + + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#if defined( NTFSDBG ) || defined( DBG ) +LONG NtfsDebugTraceIndent = 0; +LONG NtfsDebugTraceLevel = DEBUG_TRACE_PROP_FSCTL; +#endif + +#define Dbg DEBUG_TRACE_PROP_FSCTL + +// +// DefaultEmptyProperty is used in place of any property that is truly not found +// + +NOT_FOUND_PROPERTY DefaultEmptyProperty = + { sizeof( DefaultEmptyProperty ), // PropertyValueLength + PID_ILLEGAL, // PropertyId + 0, // PropertyNameLength + 0, // PropertyName[0] (alignment pad when name length == 0) + VT_EMPTY }; // dwType + +LONGLONG Views0 = 0i64; + + +NTSTATUS +ViewFileSystemControl ( + IN PIRP_CONTEXT IrpContext, + IN OBJECT_HANDLE Object, + IN ATTRIBUTE_HANDLE Attribute, + IN ULONG FsControlCode, + IN ULONG InBufferLength, + IN PVOID InBuffer, + OUT PULONG OutBufferLength, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine dispatches the file system control call to workers within + VIEWS. This routines does not complete or post the IRP; that function + is reserved for the FsCtl dispatcher in Ntfs. + + Its job is to verify that the FsControlCode, the parameters and the buffers + are appropriate. + +Arguments: + + IrpContext - IrpContext for the call + + Object - FCB of the file where the View/Property call is directed + * + Attribute - SCB of the property set stream. + + FsControlCode - Control code directing the action to take place + + InBufferLength - Length of the command buffer + + InBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally. + + OutBufferLength - pointer to ULONG length of output buffer. This value + is set on return to indicate the total size of data within the output + buffer. + + OutBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PROPERTY_CONTEXT Context; + + DebugTrace( 0, Dbg, ("ViewFileSystemControl: IrpContext %08x\n", IrpContext)); + DebugTrace( 0, Dbg, (" Object %08x\n", Object)); + DebugTrace( 0, Dbg, (" Attribute %08x\n", Attribute)); + DebugTrace( 0, Dbg, (" FsControlCode %08x\n", FsControlCode)); + DebugTrace( 0, Dbg, (" InBufferLength %08x\n", InBufferLength)); + DebugTrace( 0, Dbg, (" InBuffer %08x\n", InBuffer)); + DebugTrace( 0, Dbg, (" *OutBufferLength %08x\n", *OutBufferLength)); + DebugTrace( 0, Dbg, (" OutBuffer %08x\n", OutBuffer)); + + + // + // Acquire object according to r/w needs of worker + // + + switch (FsControlCode) { + case FSCTL_READ_PROPERTY_DATA: + case FSCTL_DUMP_PROPERTY_DATA: + NtfsAcquireSharedScb( IrpContext, Attribute ); + break; + + case FSCTL_WRITE_PROPERTY_DATA: + case FSCTL_INITIALIZE_PROPERTY_DATA: + NtfsAcquireExclusiveScb( IrpContext, Attribute ); + break; + default: + return STATUS_INVALID_DEVICE_REQUEST; + } + + if (!FlagOn( Attribute->ScbState, SCB_STATE_MODIFIED_NO_WRITE )) { + + DbgPrint( "Enabling NO_WRITE behaviour on stream\n" ); + SetFlag( Attribute->ScbState, SCB_STATE_MODIFIED_NO_WRITE ); + + } + + + try { + + // + // Make sure that the workers can map the stream + // + + if (Attribute->FileObject == NULL) { + NtfsCreateInternalAttributeStream( IrpContext, Attribute, FALSE ); + } + + // + // Set up context for workers. + // + + InitializePropertyContext( &Context, IrpContext, Object, Attribute ); + + + // + // Verify that we have a property set to begin with + // + + if (Attribute->AttributeTypeCode != $PROPERTY_SET) { + ExRaiseStatus( STATUS_PROPSET_NOT_FOUND ); + } + + switch (FsControlCode) { + case FSCTL_READ_PROPERTY_DATA: + ReadPropertyData( &Context, + InBufferLength, InBuffer, + OutBufferLength, OutBuffer ); + break; + + case FSCTL_WRITE_PROPERTY_DATA: + WritePropertyData( &Context, + InBufferLength, InBuffer, + OutBufferLength, OutBuffer ); + break; + + case FSCTL_INITIALIZE_PROPERTY_DATA: + InitializePropertyData( &Context ); + break; + + case FSCTL_DUMP_PROPERTY_DATA: + DumpPropertyData( &Context ); + break; + + } + } except ( EXCEPTION_EXECUTE_HANDLER ) { + // + // Remap the status code + // + + Status = GetExceptionCode( ); + if (Status == STATUS_ACCESS_VIOLATION) { + Status = STATUS_INVALID_USER_BUFFER; + } + } + + // + // Clean up any mapped context + // + + CleanupPropertyContext( &Context ); + + // + // Release Resource + // + + NtfsReleaseScb( IrpContext, Attribute ); + + if (Status != STATUS_SUCCESS) { + ExRaiseStatus( Status ); + } + + DebugTrace( 0, Dbg, ("ViewFileSystemControl returned status %x, length %x\n", Status, *OutBufferLength) ); + + return STATUS_SUCCESS; +} + + +/*++ + +Routine Description: + + This is the unload routine for the Views/properties file + system device driver. This routine deregisters with ntfs. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. + +--*/ + +VOID +DriverUnload ( + IN struct _DRIVER_OBJECT *DriverObject + ) +{ + NTSTATUS Status = NtOfsRegisterCallBacks( Views, NULL ); + + UNREFERENCED_PARAMETER( DriverObject ); + + DebugTrace( 0, Dbg, ("DriverUnload: NtOfsRegisterCallBacks returned %x\n", Status) ); +} + +/*++ + +Routine Description: + + This is the initialization routine for the Views/properties file + system device driver. This routine registers with ntfs and registers + an unload routine. + + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +VIEW_CALL_BACK ViewCallBackTable = { + VIEW_CURRENT_INTERFACE_VERSION, + ViewFileSystemControl + }; + +NTSTATUS +DriverEntry ( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING pstrRegistry + ) +{ + NTSTATUS Status; + + UNREFERENCED_PARAMETER( DriverObject ); + UNREFERENCED_PARAMETER( pstrRegistry ); + + Status = NtOfsRegisterCallBacks( Views, &ViewCallBackTable ); + + DriverObject->DriverUnload = DriverUnload; + + DbgPrint( "Ntfs views: Register call backs Status = 0x%lx\n", Status ); + return Status; +} + + +#if DBG == 1 +PDRIVER_INITIALIZE pdi = DriverEntry; // type check for changed prototype +#endif + + diff --git a/private/ntos/cntfs/views/heap.c b/private/ntos/cntfs/views/heap.c new file mode 100644 index 000000000..170650e5f --- /dev/null +++ b/private/ntos/cntfs/views/heap.c @@ -0,0 +1,506 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + heap.c + +Abstract: + + This module contains the code to manipulate the value heap. + + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#define Dbg DEBUG_TRACE_PROP_FSCTL + +// +// Global illegal value +// + +PROPID IllegalId = PID_ILLEGAL; + + +ULONG +FindStringInHeap( + IN PPROPERTY_CONTEXT Context, + IN PCOUNTED_STRING Name + ) + +/*++ + +Routine Description: + + This routines looks up a property by name in the property set heap. + +Arguments: + + Context - Context of heap to scan + + Name - UNICODE string property to search for + +Return Value: + + Offset to property value. If name is not found, 0 is returned + +--*/ + +{ + PPROPERTY_HEAP_ENTRY HeapEntry = FIRST_HEAP_ENTRY( Context->HeapHeader ); + UNICODE_STRING First, Second; + + First.Buffer = COUNTED_STRING_TEXT( Name ); + First.Length = Second.Length = COUNTED_STRING_LENGTH( Name ); + + // + // While there are more heap records + // + + while (!IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) { + + // + // if a name doesn't have the same length, skip it + // + + if (HeapEntry->PropertyNameLength != Name->Length) { + + NOTHING; + + // + // Same length, test for case-insignificant match + // + + } else { + Second.Buffer = &HeapEntry->PropertyName[0]; + + if (RtlCompareUnicodeString( &First, &Second, TRUE ) == 0) { + + // + // return pointer to name + // + + DebugTrace( 0, Dbg, ("FindStringInHeap %.*ws %x\n", + COUNTED_STRING_LENGTH( Name ) / sizeof( WCHAR ), + COUNTED_STRING_TEXT( Name ), + HeapOffset( Context, HeapEntry )) ); + + return HeapOffset( Context, HeapEntry ); + } + } + + // + // advance to next heap record + // + + HeapEntry = NEXT_HEAP_ENTRY( HeapEntry ); + } + + // + // return not-found + // + + DebugTrace( 0, Dbg, ("FindStringInHeap %.*ws not found\n", + COUNTED_STRING_LENGTH( Name ) / sizeof( WCHAR ), + COUNTED_STRING_TEXT( Name )) ); + + return 0; +} + + +// +// Local support routine +// + +PPROPERTY_HEAP_ENTRY GrowHeap ( + IN PPROPERTY_CONTEXT Context, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routines grows the heap to accommodate a value of a particular length. + There is no guarantee about any "extra space" present. + +Arguments: + + Context - context of property set + + Length - minimum growth needed + + +Return Value: + + Pointer to new free item at end of heap + +--*/ + +{ + PPROPERTY_HEAP_ENTRY HeapEntry; + + // + // Determine true growth. For now, we allocate 2X the size of the property + // up to a max of 1K. After that, we just allocate the requested size + // + + if (Length <= 512) { + Length = max( 2 * Length, Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 )); + } + + // + // Resize stream to account for new growth + // + + + DebugTrace( 0, Dbg, ("Growing heap to length %x\n", Length + NtOfsQueryLength( Context->Attribute ))); + + NtOfsSetLength ( + Context->IrpContext, + Context->Attribute, + Length + NtOfsQueryLength( Context->Attribute )); + + // + // Set up new header for new growth, Id and Length. + // + + HeapEntry = (PPROPERTY_HEAP_ENTRY) Add2Ptr( Context->HeapHeader, Context->HeapHeader->PropertyHeapLength ); + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyId ), + sizeof( PROPID ), + &IllegalId ); + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyValueLength ), + sizeof( Length ), + &Length ); + + // + // Resize heap + // + + Length+= Context->HeapHeader->PropertyHeapLength; + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &Context->HeapHeader->PropertyHeapLength ), + sizeof( Length ), + &Length ); + + return HeapEntry; +} + +VOID +SetValueInHeap( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_HEAP_ENTRY HeapEntry, + IN PROPID Id, + IN USHORT NameLength, + IN PWCHAR Name, + IN ULONG ValueLength, + IN SERIALIZEDPROPERTYVALUE *Value + ) + +/*++ + +Routine Description: + + This routines sets a specific value in the heap. + +Arguments: + + Context - context of property set + + HeapEntry - pointer to header that will be modified + + Id - PROPID to set + + NameLength - length of name in BYTES + + Name - pointer to name, may be NULL + + ValueLength - count of bytes in value + + Value - Serialized property value + +Return Value: + + None. + +--*/ + +{ + PROPASSERT( Id != PID_ILLEGAL ); + + // + // Set PropertyId + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyId ), + sizeof( PROPID ), + &Id ); + + // + // Set name length + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyNameLength ), + sizeof( USHORT ), + &NameLength ); + + // + // Set name if present + // + + if (Name != NULL) { + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyName[0] ), + NameLength, + Name ); + } + + // + // Set property value + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, PROPERTY_HEAP_ENTRY_VALUE( HeapEntry )), + ValueLength, + Value ); + +} + + +ULONG +AddValueToHeap( + IN PPROPERTY_CONTEXT Context, + IN PROPID Id, + IN ULONG Length, + IN USHORT NameLength, + IN PWCHAR Name OPTIONAL, + IN ULONG ValueLength, + IN SERIALIZEDPROPERTYVALUE *Value + ) + +/*++ + +Routine Description: + + This routines adds a value to the heap. We walk the heap looking for a free + header (PID_ILLEGAL) whose size is sufficient to contain the name and value. + If one is not found, the heap and attribute are grown. + +Arguments: + + Context - context of property set + + Id - PROPID to add + + Length - length of entire propery value (including name) in bytes + + NameLength - length of name in BYTES + + Name - pointer to name, may be NULL + + ValueLength - count of bytes in value + + Value - Serialized property value + +Return Value: + + Offset to property value stored in heap + +--*/ + +{ + PPROPERTY_HEAP_ENTRY HeapEntry = FIRST_HEAP_ENTRY( Context->HeapHeader ); + + PROPASSERT( Id != PID_ILLEGAL ); + + DebugTrace( +1, Dbg, ("AddValueToHeap(%x '%.*ws' %x len %x)\n", + Id, NameLength / sizeof( WCHAR ), Name, Value, ValueLength) ); + DebugTrace( 0, Dbg, ("property value length is %x\n", Length) ); + + // + // Walk through the heap until we reach the end or until we have + // a free block that contains enough room for this property. We also + // will break out if the block we find has enough room for the property + // and can be split to allow for a property that has 5 chars of name + // and 8 bytes of serialized value. + // + + while (TRUE) { + // + // If this is not a valid header, break + // + + if (IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) { + break; + } + + // + // if this is a free header + // + + if (HeapEntry->PropertyId == PID_ILLEGAL) { + + // + // If the value length matches exactly, break + // + + if (HeapEntry->PropertyValueLength == Length) { + break; + } + + // + // If the value length allows for an extra value, break + // + + if (HeapEntry->PropertyValueLength >= + Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 )) { + break; + } + } + + // + // Go to next block + // + + HeapEntry = NEXT_HEAP_ENTRY( HeapEntry ); + } + + // + // If we are at the end of the heap, then we must grow the heap. In this case + // we grow the heap by a large block and set thing up as if we had found + // a free block of the right size. We then fall into the normal reuse-free- + // block code. + // + + if (IS_LAST_HEAP_ENTRY( Context->HeapHeader, HeapEntry )) { + HeapEntry = GrowHeap( Context, Length ); + } + + // + // HeapEntry points to a free heap block that is large enough to contain + // the new length, possibly splitting. First, split if necessary. + // + + if (HeapEntry->PropertyValueLength != Length) { + PPROPERTY_HEAP_ENTRY SecondHeader; + ULONG SecondLength = HeapEntry->PropertyValueLength - Length; + + PROPASSERT( Length + PROPERTY_HEAP_ENTRY_SIZE( 10, 8 ) <= HeapEntry->PropertyValueLength); + + // + // Get address of second block header + // + + SecondHeader = (PPROPERTY_HEAP_ENTRY) Add2Ptr( HeapEntry, Length ); + + // + // set up second block header: length and propid + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &SecondHeader->PropertyId ), + sizeof( PROPID ), + &IllegalId ); + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &SecondHeader->PropertyValueLength ), + sizeof( SecondLength ), + &SecondLength ); + + + // + // set up value header length to reflect split + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyValueLength ), + sizeof( Length ), + &Length ); + + } + + // + // HeapEntry points at a free block with exactly the right amount of space + // for this name/value + // + + PROPASSERT( Length == HeapEntry->PropertyValueLength ); + + SetValueInHeap( Context, HeapEntry, Id, NameLength, Name, ValueLength, Value ); + + DebugTrace( -1, Dbg, ("value offset is at %x\n", + HeapOffset( Context, HeapEntry )) ); + + return HeapOffset( Context, HeapEntry ); +} + + +VOID +DeleteFromHeap( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_HEAP_ENTRY HeapEntry + ) + +/*++ + +Routine Description: + + This routines deletes a value from the heap. The present implementation + marks the header as being for PID_ILLEGAL. + +Arguments: + + Context - Property set context + + HeapEntry - entry to be deleted + +Return Value: + + Nothing + +--*/ + +{ + DebugTrace( 0, Dbg, ("DeleteFromHeap at %x\n", HeapEntry) ); + + // + // Delete by setting the value to be PID_ILLEGAL + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + ContextOffset( Context, &HeapEntry->PropertyId), + sizeof( IllegalId ), + &IllegalId ); +} + diff --git a/private/ntos/cntfs/views/initprop.c b/private/ntos/cntfs/views/initprop.c new file mode 100644 index 000000000..c1ab2066a --- /dev/null +++ b/private/ntos/cntfs/views/initprop.c @@ -0,0 +1,122 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + initprop.h + +Abstract: + + This module contains the initialization user FsCtls for the Ntfs Property + support. + + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#define Dbg DEBUG_TRACE_PROP_FSCTL + + +VOID +InitializePropertyData ( + IN PPROPERTY_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine initializes a blank stream for property access. + + We set up the initial size, lay out an empty table and empty header. + + +Arguments: + + Context - Property Context for the call + +Return Value: + + Nothing + +--*/ +{ + PROPERTY_SET_HEADER PropertySetHeader; + PROPERTY_ID_TABLE IdTable; + PROPERTY_HEAP_HEADER HeapHeader; + + // + // Set up header + // + + PropertySetHeader.wByteOrder = 0xFFFE; + PropertySetHeader.wFormat = PSH_FORMAT_VERSION; + PropertySetHeader.dwOSVer = PSH_DWOSVER; + RtlZeroMemory( &PropertySetHeader.clsid, sizeof( CLSID )); + PropertySetHeader.reserved = 2; // BUGBUG ??? + PropertySetHeader.IdTableOffset = LongAlign( sizeof( PROPERTY_SET_HEADER )); + PropertySetHeader.ValueHeapOffset = PropertySetHeader.IdTableOffset; + + // + // Set up Id table + // + + IdTable.PropertyCount = 0; + IdTable.MaximumPropertyCount = PIT_PROPERTY_DELTA; + PropertySetHeader.ValueHeapOffset += + LongAlign( PROPERTY_ID_TABLE_SIZE( PIT_PROPERTY_DELTA )); + + // + // Set up Heap header + // + + HeapHeader.PropertyHeapLength = PHH_INITIAL_SIZE; + HeapHeader.PropertyHeapEntry[0].PropertyValueLength = PHH_INITIAL_SIZE - PROPERTY_HEAP_HEADER_SIZE; + HeapHeader.PropertyHeapEntry[0].PropertyId = PID_ILLEGAL; + HeapHeader.PropertyHeapEntry[0].PropertyNameLength = 0; + + // + // Set the new size of the stream + // + + NtOfsSetLength( Context->IrpContext, Context->Attribute, + PropertySetHeader.ValueHeapOffset + PHH_INITIAL_SIZE ); + + + // + // Write out the header + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + 0, + sizeof( PROPERTY_SET_HEADER ), + &PropertySetHeader ); + + + // + // Write out the table + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + PropertySetHeader.IdTableOffset, + sizeof( PROPERTY_ID_TABLE ), + &IdTable ); + + // + // Write out the heap and set the stream size + // + + LogFileFullFailCheck( Context->IrpContext ); + NtOfsPutData( Context->IrpContext, + Context->Attribute, + PropertySetHeader.ValueHeapOffset, + sizeof( PROPERTY_HEAP_HEADER ), + &HeapHeader ); +} + diff --git a/private/ntos/cntfs/views/makefile b/private/ntos/cntfs/views/makefile new file mode 100644 index 000000000..0e21f828e --- /dev/null +++ b/private/ntos/cntfs/views/makefile @@ -0,0 +1,13 @@ +############################################################################ +# +# Copyright (C) 1992, Microsoft Corporation. +# +# All rights reserved. +# +############################################################################ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/cntfs/views/readprop.c b/private/ntos/cntfs/views/readprop.c new file mode 100644 index 000000000..859a39ece --- /dev/null +++ b/private/ntos/cntfs/views/readprop.c @@ -0,0 +1,802 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + read.h + +Abstract: + + This module contains the implementation of the read property data FsCtl + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#define Dbg DEBUG_TRACE_PROP_FSCTL + + +PPROPERTY_INFO +BuildPropertyInfoFromPropSpec ( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_SPECIFICATIONS Specs, + IN PVOID InBufferEnd, + IN PROPID NextId + ) + +/*++ + +Routine Description: + + This routine allocates and builds PROPERTY_INFO from the input + specifications. It looks up the names or id's in the property set + and assigns Ids if necessary. + +Arguments: + + Context - property set context + + Specs - input property specifications + + InBufferEnd - end of input buffer for bounds testing Specs + + NextId - next property Id to assign when a name is not found + +Return Value: + + PPROPERTY_INFO pointer to allocated and initialized structure. + +--*/ +{ + PPROPERTY_INFO Info; + ULONG i; + ULONG ValueOffset; + PPROPERTY_HEAP_ENTRY HeapEntry; + PROPID Id; + + Info = NtfsAllocatePool( PagedPool, PROPERTY_INFO_SIZE( Specs->Count )); + + // + // Set up header and running totals + // + + Info->Count = Specs->Count; + Info->TotalIdsSize = PROPERTY_IDS_SIZE( Info->Count ); + Info->TotalValuesSize = PROPERTY_VALUES_SIZE( Info->Count ); + Info->TotalNamesSize = PROPERTY_NAMES_SIZE( Info->Count ); + + // + // Indirect information is not determined until we finish + // scanning the array + // + + Info->IndirectCount = 0; + Info->TotalIndirectSize = 0; + + for (i = 0; i < Info->Count; i++) { + + // + // If the spec is an ID, then look up the Id in the table + // + + if (Specs->Specifiers[i].Variant == PRSPEC_PROPID) { + ValueOffset = FindIdInTable( Context, PROPERTY_SPECIFIER_ID( Specs, i )); + + // + // Otherwise, find the name string, verify it's within the buffer + // and look it up in the heap. + // + + } else { + PCOUNTED_STRING Name = PROPERTY_SPECIFIER_COUNTED_STRING( Specs, i ); + + if (InBufferEnd < Add2Ptr(Name, COUNTED_STRING_SIZE( Name->Length ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + ValueOffset = FindStringInHeap( Context, Name ); + } + + // + // Convert offset to pointer to header + // + + HeapEntry = + ValueOffset == 0 ? EMPTY_PROPERTY + : GET_HEAP_ENTRY( Context->HeapHeader, ValueOffset ); + + // + // Gather up the size of this output + // + + // TotalIds is already set up + Info->TotalValuesSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + Info->TotalNamesSize += HeapEntry->PropertyNameLength; + if (IS_INDIRECT_VALUE( HeapEntry )) { + Info->IndirectCount++; + Info->TotalIndirectSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + } + + // + // Set up the Entry pointer and Id. If we did not find a property, then + // attempt to assign and Id. + // + + Info->Entries[i].Heap = HeapEntry; + if (HeapEntry == EMPTY_PROPERTY) { + + // + // No property was found. If a propid was specified, go use it + // + + if (Specs->Specifiers[i].Variant == PRSPEC_PROPID) { + + Id = PROPERTY_SPECIFIER_ID( Specs, i ); + + // + // No Id was specified, assign one if we are supposed to. We could + // probe the table until we don't find and Id, but better to let + // the table tell us where the next hole is. + // + + } else if (NextId != PID_ILLEGAL) { + Id = FindFreeIdInTable( Context, NextId ); + NextId = Id + 1; + } else { + Id = PID_ILLEGAL; + } + } else { + Id = HeapEntry->PropertyId; + } + Info->Entries[i].Id = Id; + } + + Info->TotalIndirectSize += INDIRECT_PROPERTIES_SIZE( Info->IndirectCount ); + + Context->Info = Info; + + return Info; +} + + +PPROPERTY_INFO +BuildPropertyInfoFromIds ( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_IDS Ids + ) + +/*++ + +Routine Description: + + This routine allocates and builds PROPERTY_INFO from the input + ids. + +Arguments: + + Context - property set context + + Ids - input property Ids + +Return Value: + + PPROPERTY_INFO pointer to allocated and initialized structure. + +--*/ +{ + PPROPERTY_INFO Info; + ULONG i; + ULONG ValueOffset; + PPROPERTY_HEAP_ENTRY HeapEntry; + + Info = NtfsAllocatePool( PagedPool, PROPERTY_INFO_SIZE( Ids->Count )); + + // + // Set up header and running totals + // + + Info->Count = Ids->Count; + Info->TotalIdsSize = PROPERTY_IDS_SIZE( Info->Count ); + Info->TotalValuesSize = PROPERTY_VALUES_SIZE( Info->Count ); + Info->TotalNamesSize = PROPERTY_NAMES_SIZE( Info->Count ); + + // + // Indirect information is not determined until we finish + // scanning the array + // + + Info->IndirectCount = 0; + Info->TotalIndirectSize = 0; + + for (i = 0; i < Info->Count; i++) { + + ValueOffset = FindIdInTable( Context, PROPERTY_ID( Ids, i )); + + // + // Convert offset to pointer to header + // + + HeapEntry = + ValueOffset == 0 ? EMPTY_PROPERTY + : GET_HEAP_ENTRY( Context->HeapHeader, ValueOffset ); + + // + // Gather up the size of this output + // + + // TotalIds is already set up + Info->TotalValuesSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + Info->TotalNamesSize += HeapEntry->PropertyNameLength; + if (IS_INDIRECT_VALUE( HeapEntry )) { + Info->IndirectCount++; + Info->TotalIndirectSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + } + + // + // Set up the Entry pointer and Id. If we did not find a property, then + // attempt to assign and Id. + // + + Info->Entries[i].Heap = HeapEntry; + Info->Entries[i].Id = PROPERTY_ID( Ids, i ); + } + + Info->TotalIndirectSize += INDIRECT_PROPERTIES_SIZE( Info->IndirectCount ); + + Context->Info = Info; + + return Info; +} + + +PPROPERTY_INFO +BuildPropertyInfoFromIdTable ( + IN PPROPERTY_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine allocates and builds PROPERTY_INFO from the property Id + table + +Arguments: + + Context - property set context + +Return Value: + + PPROPERTY_INFO pointer to allocated and initialized structure. + +--*/ +{ + PPROPERTY_INFO Info; + ULONG i; + ULONG ValueOffset; + PPROPERTY_HEAP_ENTRY HeapEntry; + + Info = NtfsAllocatePool( PagedPool, PROPERTY_INFO_SIZE( Context->IdTable->PropertyCount )); + + // + // Set up header and running totals + // + + Info->Count = Context->IdTable->PropertyCount; + Info->TotalIdsSize = PROPERTY_IDS_SIZE( Info->Count ); + Info->TotalValuesSize = PROPERTY_VALUES_SIZE( Info->Count ); + Info->TotalNamesSize = PROPERTY_NAMES_SIZE( Info->Count ); + + // + // Indirect information is not determined until we finish + // scanning the array + // + + Info->IndirectCount = 0; + Info->TotalIndirectSize = 0; + + for (i = 0; i < Info->Count; i++) { + + HeapEntry = + GET_HEAP_ENTRY( Context->HeapHeader, + Context->IdTable->Entry[i].PropertyValueOffset ); + + // + // Gather up the size of this output + // + + // TotalIds is already set up + Info->TotalValuesSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + Info->TotalNamesSize += HeapEntry->PropertyNameLength; + if (IS_INDIRECT_VALUE( HeapEntry )) { + Info->IndirectCount++; + Info->TotalIndirectSize += PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + } + + // + // Set up the Entry pointer and Id. If we did not find a property, then + // attempt to assign and Id. + // + + Info->Entries[i].Heap = HeapEntry; + Info->Entries[i].Id = HeapEntry->PropertyId; + } + + Info->TotalIndirectSize += INDIRECT_PROPERTIES_SIZE( Info->IndirectCount ); + + Context->Info = Info; + + return Info; +} + + +PVOID +BuildPropertyIds ( + IN PPROPERTY_INFO Info, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine builds a PROPERTY_IDS structure in the output + buffer from the array of value headers. + +Arguments: + + Info - pointers to the properties + + OutBuffer - pointer to the unverified user command buffer. Since we + address this directly, we may raise if the buffer becomes invalid. + The caller must have already verified the size. + +Return Value: + + PVOID pointer to next output + +--*/ +{ + ULONG i; + + // + // Build Ids + // + + PPROPERTY_IDS Ids = (PPROPERTY_IDS) OutBuffer; + + PROPASSERT( OutBuffer == (PVOID)LongAlign( OutBuffer )); + + Ids->Count = Info->Count; + + for (i = 0; i < Info->Count; i++) { + Ids->PropertyIds[i] = PROPERTY_INFO_ID( Info, i ); + } + + return (PVOID) Add2Ptr( Ids, PROPERTY_IDS_SIZE( Info->Count )); +} + + +PVOID +BuildPropertyNames ( + IN PPROPERTY_INFO Info, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine builds a PROPERTY_NAMES structure in the output + buffer from the array of value headers. + +Arguments: + + Info - pointers to the properties + + OutBuffer - pointer to the unverified user command buffer. Since we + address this directly, we may raise if the buffer becomes invalid. + The caller must have already verified the size. + +Return Value: + + PVOID pointer to next output + +--*/ +{ + ULONG i; + + // + // Build property names + // + + PPROPERTY_NAMES Names = (PPROPERTY_NAMES) OutBuffer; + + PROPASSERT( OutBuffer == (PVOID)LongAlign( OutBuffer )); + + Names->Count = Info->Count; + OutBuffer = Add2Ptr( Names, PROPERTY_NAMES_SIZE( Info->Count )); + + // + // For each property found, copy the name into the buffer and + // advance for next output. + // + + for (i = 0; i < Info->Count; i++) { + ULONG Length = PROPERTY_INFO_HEAP_ENTRY( Info, i )->PropertyNameLength; + Names->PropertyNameOffset[i] = PtrOffset( Names, OutBuffer ); + + PROPASSERT( OutBuffer == (PVOID)WordAlign( OutBuffer )); + + RtlCopyMemory( OutBuffer, + &PROPERTY_INFO_HEAP_ENTRY( Info, i )->PropertyName[0], + Length); + OutBuffer = Add2Ptr( OutBuffer, Length); + } + + // + // Set last pointer and length to reflect total size of the + // buffer. + // + + Names->Length = Names->PropertyNameOffset[i] = PtrOffset( Names, OutBuffer ); + + return OutBuffer; +} + + +PVOID +BuildPropertyValues ( + IN PPROPERTY_INFO Info, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine builds a PROPERTY_VALUES structure in the output + buffer from the array of value headers. + +Arguments: + + Info - pointer to properties + + OutBuffer - pointer to the unverified user command buffer. Since we + address this directly, we may raise if the buffer becomes invalid. + The caller must have already verified the size. + +Return Value: + + PVOID pointer to next output + +--*/ +{ + ULONG i; + + // + // Build property values header + // + + PPROPERTY_VALUES Values = (PPROPERTY_VALUES) OutBuffer; + Values->Count = Info->Count; + + PROPASSERT( OutBuffer == (PVOID)LongAlign( OutBuffer )); + + OutBuffer = Add2Ptr( Values, PROPERTY_VALUES_SIZE( Info->Count )); + + // + // For each property found, copy the value into the buffer and + // advance for next output. + // + + for (i = 0; i < Info->Count; i++) { + ULONG Length = PROPERTY_HEAP_ENTRY_VALUE_LENGTH( PROPERTY_INFO_HEAP_ENTRY( Info, i )); + Values->PropertyValueOffset[i] = PtrOffset( Values, OutBuffer ); + + PROPASSERT( OutBuffer == (PVOID)LongAlign( OutBuffer )); + + RtlCopyMemory( OutBuffer, + PROPERTY_HEAP_ENTRY_VALUE( PROPERTY_INFO_HEAP_ENTRY( Info, i )), + Length); + OutBuffer = Add2Ptr( OutBuffer, Length); + } + + // + // Set last pointer and length to reflect total size of the + // buffer. + // + + Values->PropertyValueOffset[i] = Values->Length = PtrOffset( Values, OutBuffer ); + + return OutBuffer; +} + + +VOID +ReadPropertyData ( + IN PPROPERTY_CONTEXT Context, + IN ULONG InBufferLength, + IN PVOID InBuffer, + OUT PULONG OutBufferLength, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine performs property read functions. + + We verify the header format and perform the read operation. + +Arguments: + + Context - Property Context for the call + + InBufferLength - Length of the command buffer + + InBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally. + + OutBufferLength - pointer to ULONG length of output buffer. This value + is set on return to indicate the total size of data within the output + buffer. + + OutBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally + +Note: + + The Io system does no validation whatsoever on either buffer. This means that + we need to probe for readability and for being a valid user address. + +Return Value: + + Nothing + +--*/ + +{ + PVOID InBufferEnd = Add2Ptr( InBuffer, InBufferLength ); + PPROPERTY_INFO Info = NULL; + + try { + PPROPERTY_READ_CONTROL Control = (PPROPERTY_READ_CONTROL) InBuffer; + ULONG ValueOffset; + PVOID NextOutput; + ULONG TotalLength; + ULONG Count; + ULONG i; + + // + // Buffers must be long aligned + // + + if ( + // + // Long alignment of all buffers + // + InBuffer != (PVOID)LongAlign( InBuffer ) || + OutBuffer != (PVOID)LongAlign( OutBuffer ) || + + // + // Room for control at least + // + InBufferEnd < (PVOID)(Control + 1) + + ) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + + } + + + if (KeGetPreviousMode() != KernelMode) { + // + // Verify that the input buffer is readable + // + + ProbeForRead( InBuffer, InBufferLength, sizeof( ULONG )); + + // + // Verify that the output buffer is readable + // + + ProbeForWrite( OutBuffer, *OutBufferLength, sizeof( ULONG )); + } + + // + // Map attribute for full size + // + + MapPropertyContext( Context ); + + + // + // Verify the property set has valid contents. + // + + CheckPropertySet( Context ); + + if (Control->Op == PRC_READ_PROP) { + PPROPERTY_SPECIFICATIONS Specs; + + // + // The input buffer is: + // PROPERTY_READ_CONTROL + // PROPERTY_SPECIFICATIONS + // + // The output buffer will be: + // PROPERTY_VALUES + // + + // + // Build value headers array from specifiers + // + + Specs = (PPROPERTY_SPECIFICATIONS) (Control + 1); + if ( + // + // Room for specifications header + // + + InBufferEnd < (PVOID)(Specs + 1) || + + // + // Room for full body + // + InBufferEnd < Add2Ptr( Specs, PROPERTY_SPECIFICATIONS_SIZE( Specs->Count )) + + ) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + + } + + Info = BuildPropertyInfoFromPropSpec( Context, + Specs, + InBufferEnd, + PID_ILLEGAL ); + + + // + // Check for enough room on output + // + + TotalLength = Info->TotalValuesSize; + + if (TotalLength > *OutBufferLength) { + PULONG ReturnLength = OutBuffer; + + *ReturnLength = TotalLength; + *OutBufferLength = sizeof( ULONG ); + ExRaiseStatus( STATUS_BUFFER_OVERFLOW ); + } + + // + // Build property values header + // + + NextOutput = BuildPropertyValues( Info, OutBuffer ); + + PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PRC_READ_NAME) { + PPROPERTY_IDS Ids; + + // + // The input buffer is: + // PROPERTY_READ_CONTROL + // PROPERTY_IDS + // + // The output buffer will be: + // PROPERTY_NAMES + // + + // + // Build offsets array from Ids + // + + Ids = (PPROPERTY_IDS) (Control + 1); + + if (InBufferEnd < Add2Ptr( Ids, PROPERTY_IDS_SIZE( 0 )) || + InBufferEnd < Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Info = BuildPropertyInfoFromIds( Context, Ids ); + + // + // Check for enough room on output + // + + TotalLength = Info->TotalNamesSize; + + if (TotalLength > *OutBufferLength) { + PULONG ReturnLength = OutBuffer; + + *ReturnLength = TotalLength; + *OutBufferLength = sizeof( ULONG ); + ExRaiseStatus( STATUS_BUFFER_OVERFLOW ); + } + + + // + // Build property names + // + + NextOutput = BuildPropertyNames( Info, OutBuffer ); + + PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PRC_READ_ALL) { + ULONG TotalIds; + ULONG TotalNames; + ULONG TotalValues; + // + // The input buffer is NULL + // + // The output buffer will be: + // PROPERTY_IDS + // PROPERTY_NAMES + // PROPERTY_VALUES + // + + Info = BuildPropertyInfoFromIdTable( Context ); + + // + // Check for enough room on output + // + + TotalLength = Info->TotalIdsSize + + LongAlign( Info->TotalNamesSize ) + + Info->TotalValuesSize; + + if (TotalLength > *OutBufferLength) { + PULONG ReturnLength = OutBuffer; + + *ReturnLength = TotalLength; + *OutBufferLength = sizeof( ULONG ); + ExRaiseStatus( STATUS_BUFFER_OVERFLOW ); + } + + // + // Build Ids + // + + NextOutput = BuildPropertyIds( Info, OutBuffer ); + PROPASSERT( NextOutput == (PVOID)LongAlign( NextOutput )); + + // + // Build property names + // + + NextOutput = BuildPropertyNames( Info, NextOutput ); + PROPASSERT( NextOutput == (PVOID)WordAlign( NextOutput )); + + // + // Build property values header + // + + NextOutput = BuildPropertyValues( Info, (PVOID)LongAlign( NextOutput )); + PROPASSERT( NextOutput == (PVOID)LongAlign( NextOutput )); + + PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + *OutBufferLength = TotalLength; + + } finally { + + if (Info != NULL) { + NtfsFreePool( Info ); + } + + } +} + diff --git a/private/ntos/cntfs/views/sources b/private/ntos/cntfs/views/sources new file mode 100644 index 000000000..9cb0eb303 --- /dev/null +++ b/private/ntos/cntfs/views/sources @@ -0,0 +1,56 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +SYNCHRONIZE_DRAIN=1 +SYNCHRONIZE_BLOCK=1 + +MAJORCOMP=ntfs +MINORCOMP=fs + +TARGETNAME=views +TARGETPATH=obj +TARGETTYPE=DRIVER + +INCLUDES=$(INCLUDES);.;..\..\inc;.. + +C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_ -D_NTIFS_ + +CAIRO_PRODUCT=1 + +SOURCES=driver.c \ + check.c \ + heap.c \ + initprop.c \ + readprop.c \ + table.c \ + writprop.c + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\cairo\*\ntfs.lib + +PRECOMPILED_INCLUDE=viewprop.h +PRECOMPILED_PCH=viewprop.pch +PRECOMPILED_OBJ=viewprop.obj + + diff --git a/private/ntos/cntfs/views/table.c b/private/ntos/cntfs/views/table.c new file mode 100644 index 000000000..eae79f15f --- /dev/null +++ b/private/ntos/cntfs/views/table.c @@ -0,0 +1,543 @@ +/*++ + +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 <viewprop.h> // 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 + ); + } + } +} diff --git a/private/ntos/cntfs/views/viewprop.h b/private/ntos/cntfs/views/viewprop.h new file mode 100644 index 000000000..2f681ad0b --- /dev/null +++ b/private/ntos/cntfs/views/viewprop.h @@ -0,0 +1,600 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + ViewProp.h + +Abstract: + + This module defines the routines used in ViewProp.Sys to service view and + property requests. + + ********************************* + *No other clients are supported.* + ********************************* + +Author: + + Mark Zbikowski [MarkZ] 21-Dec-95 + +Revision History: + + +--*/ + +#include <ntfsproc.h> + +#include <ntrtl.h> +#include <nturtl.h> // needs ntrtl.h +#include <objidl.h> // needs nturtl.h +#include <propset.h> // needs objidl.h +#include "ntfsprop.h" // needs objidl.h + +// +// Big picture of Views implementation +// +// Directories are the only objects that contain views. Views' purpose is +// to provide an up-to-date sorted list of objects in a directory. This is +// only single-level containment as we don't do transitive closure. +// +// A view is simply an index whose entries are an ordered list of property +// values. Each view has a description that lists the sources of each +// property value. +// +// All views of a directory are stored as index attributes of the directory +// itself. All view descriptsions are stored in a single data sttribute of +// the directory. +// +// Creation of a view consists of: +// Dispatched via FsCtl +// Acquiring the directory +// Creating/opening the view description attribute +// Adding a record describing the view (what properties are included, +// which ones are sorted, and whether the sort is up or down). This +// is a logged operation. +// Creating the view index +// Releasing the directory +// +// Deleting a view consists of: +// Dispatched via FsCtl +// Acquiring the directory +// Opening the view description attribute +// Finding/deleting the record describing the view. This is a logged +// operation +// Deleting the view index +// Releasing the directory +// +// Updating a view consists of: +// Dispatched via property change call, security change call, +// DUPLICATED_INFO change, or STAT_INFO change +// Acquiring the object +// Acquiring the parent directory +// Opening the view description +// For each view that contains a property that is being changed +// From the object, build the old row and build the new row +// Open the view +// Delete the old row +// Insert the new row +// Releasing the parent +// Releasing the object +// + + +#if DBG +#define PROPASSERT(exp) \ + ((exp) ? TRUE : \ + (DbgPrint( "%s:%d %s\n",__FILE__,__LINE__,#exp ), \ + DbgBreakPoint(), \ + TRUE)) +#else // DBG +#define PROPASSERT(exp) +#endif + +// +// Property set storage format +// +// Each property set is stored within a single stream and is limited to +// VACB_MAPPING_GRANULARITY in size. The storage format is optimized for +// the following operations: +// +// Write all properties. (written via save/save-all/copy/restore) +// Write in place. +// Write and extend length of variable length property. +// Add one or several new properties. +// Delete all properties. +// Delete one or several properties. +// +// Read all properties. (open/copy/backup) +// Read one or several properties. +// +// Name via ID. +// Name via string. +// +// Each property set is comprised of three pieces: a fixed-size header, +// a property ID table and a property-value heap. +// +// The header describes the sizes and offsets of the table and heap within the +// stream. The header is always at offset 0i64. Planning for a future where +// this format might be exposed to user-space code, the header is included from +// the OLE property set format and has several additional fields. +// + +typedef struct _PROPERTY_SET_HEADER { + + // + // Header from OLE describing version number and containing fields + // for format and class Id + // + + PROPERTYSETHEADER; + + // + // Offset to PropertyIdTable + // + + ULONG IdTableOffset; + + // + // Offset to PropertyValueHeap + // + + ULONG ValueHeapOffset; + +} PROPERTY_SET_HEADER, *PPROPERTY_SET_HEADER; +typedef const PROPERTY_SET_HEADER *PCPROPERTY_SET_HEADER; + +#define PSH_FORMAT_VERSION 2 +#define PSH_DWOSVER 0x00020005 + +#define PROPERTY_ID_TABLE(psh) \ + ((PPROPERTY_ID_TABLE)Add2Ptr( (psh), (psh)->IdTableOffset )) +#define PROPERTY_HEAP_HEADER(psh) \ + ((PPROPERTY_HEAP_HEADER)Add2Ptr( (psh), (psh)->ValueHeapOffset )) + + +// +// Following the header in the stream is the PropertyIdTable. +// +// The Property Id Table allows for a quick mapping of PropertyId to offset +// within the Property Heap. The table is a sorted (on PropertyId) array of +// Id/Offset pairs. The table is allowed to contain some extra slots so that +// insertion of a new element can often be made without shuffling the heap. +// +// As an implementation efficiency, the Entry array is addressed in the code in +// 1-based fashion. The array, however, will always occupy entry [0]. +// + +typedef struct _PROPERTY_TABLE_ENTRY { + + // + // Property ID used for sorting + // + + ULONG PropertyId; + + // + // Offset within the property heap of the value of this property + // + + ULONG PropertyValueOffset; + +} PROPERTY_TABLE_ENTRY, *PPROPERTY_TABLE_ENTRY; + + +typedef struct _PROPERTY_ID_TABLE { + + // + // Number of entries that are currently in the table + // + + ULONG PropertyCount; + + // + // Maximum number of entries that the table could contain + // + + ULONG MaximumPropertyCount; + + // + // Beginning of the table itself + // + + PROPERTY_TABLE_ENTRY Entry[1]; + +} PROPERTY_ID_TABLE, *PPROPERTY_ID_TABLE; + +typedef const PROPERTY_ID_TABLE *PCPROPERTY_ID_TABLE; + +#define PIT_PROPERTY_DELTA 0x10 + +#define PROPERTY_ID_TABLE_SIZE(c) \ + (VARIABLE_STRUCTURE_SIZE( PROPERTY_ID_TABLE, PROPERTY_TABLE_ENTRY, c )) +#define PROPERTY_ID_ENTRY(p,i) \ + ((p)->Entry[i]) + + +// +// Following the PropertyIdTable in the stream is the PropertyValueHeap +// +// The PropertyValueHeap is a boundary-tagged heap. The contents of each +// heap element contains the Length of the element, PropertyId of the element, +// the unicode string name, if one has been assigned, and the serialized property +// value of the property. +// +// The length of the element may be larger than the data contained within it. This +// enables replacement of long values with shorter ones without forcing +// reallocation. When reallocation must take place, the existing heap item is +// marked with an invalid property Id and a new item is allocated at the end of the +// heap. +// +// Over time, this may result in unused space within the heap. When writing a +// property set for the first time, the total amount of free space is calculated +// and stored in the SCB. If that amount is either > 4K or >20% of the size of the +// stream, a compaction is done. +// +// The serialized format of property values is dictated by OLE. This results in a +// common set of source to manipulate the serialized formats. +// + +typedef struct _PROPERTY_HEAP_ENTRY { + + // + // Length of this value in bytes + // + + ULONG PropertyValueLength; + + // + // Property Id for this heap item. This is used for updating the Property + // Table during compaction. + // + + ULONG PropertyId; + + // + // Length in bytes of the string name. If zero, then no name + // is assigned. + // + + USHORT PropertyNameLength; + + // + // Name, if present + // + + WCHAR PropertyName[1]; + + // + // Following the name, on a DWORD boundary is the SERIALIZEDPROPERTYVALUE. + // + +} PROPERTY_HEAP_ENTRY, *PPROPERTY_HEAP_ENTRY; + +#define PROPERTY_HEAP_ENTRY_SIZE(n,v) \ + (LongAlign( sizeof( PROPERTY_HEAP_ENTRY ) + (n)) + (v)) +#define PROPERTY_HEAP_ENTRY_VALUE(p) \ + ((SERIALIZEDPROPERTYVALUE *) LongAlign( Add2Ptr( &(p)->PropertyName[0], (p)->PropertyNameLength ))) +#define PROPERTY_HEAP_ENTRY_VALUE_LENGTH(p) \ + ((p)->PropertyValueLength - PtrOffset( (p), PROPERTY_HEAP_ENTRY_VALUE( p ))) +#define IS_INDIRECT_VALUE(p) \ + (IsIndirectVarType( PROPERTY_HEAP_ENTRY_VALUE( (p) )->dwType )) + + + +// +// The heap has a header as well, that contains the total size of the heap +// + +typedef struct _PROPERTY_HEAP_HEADER { + + // + // Length of the heap, including this structure, in bytes. This must + // never span beyond the end of data in the stream. + // + + ULONG PropertyHeapLength; + + // + // First PropertyHeapEntry + // + + PROPERTY_HEAP_ENTRY PropertyHeapEntry[1]; + +} PROPERTY_HEAP_HEADER, *PPROPERTY_HEAP_HEADER; +typedef const PROPERTY_HEAP_HEADER *PCPROPERTY_HEAP_HEADER; + +#define PROPERTY_HEAP_HEADER_SIZE \ + (sizeof( PROPERTY_HEAP_HEADER ) - sizeof( PROPERTY_HEAP_ENTRY )) + +#define PHH_INITIAL_SIZE 0x80 + +#define GET_HEAP_ENTRY(phh,off) \ + ((PPROPERTY_HEAP_ENTRY) Add2Ptr( (phh), (off) )) +#define FIRST_HEAP_ENTRY(phh) \ + (&(phh)->PropertyHeapEntry[0]) +#define NEXT_HEAP_ENTRY(pvh) \ + ((PPROPERTY_HEAP_ENTRY) Add2Ptr( (pvh), (pvh)->PropertyValueLength )) +#define IS_LAST_HEAP_ENTRY(phh,pvh) \ + ((PCHAR)(pvh) >= (PCHAR)(phh) + (phh)->PropertyHeapLength) + + +// +// Debug levels for printing +// + +#define DEBUG_TRACE_PROP_FSCTL 0x00000001 +#define DEBUG_TRACE_READ_PROPERTY 0x00000002 +#define DEBUG_TRACE_WRITE_PROPERTY 0x00000004 + + + +// +// Runtime structures. +// + +// +// PROPERTY_INFO is a structure built out of the input, either from +// PROPERTY_SPECIFIERS or PROPERTY_IDS. +// + +typedef struct _PROPERTY_INFO_ENTRY { + PPROPERTY_HEAP_ENTRY Heap; + PROPID Id; +} PROPERTY_INFO_ENTRY; + +typedef struct _PROPERTY_INFO { + ULONG Count; + ULONG TotalIdsSize; + ULONG TotalValuesSize; + ULONG TotalNamesSize; + ULONG TotalIndirectSize; + ULONG IndirectCount; + PROPERTY_INFO_ENTRY Entries[1]; +} PROPERTY_INFO, *PPROPERTY_INFO; + +#define PROPERTY_INFO_SIZE(c) \ + (VARIABLE_STRUCTURE_SIZE( PROPERTY_INFO, PROPERTY_INFO_ENTRY, c )) + +#define PROPERTY_INFO_HEAP_ENTRY(p,i) \ + ((p)->Entries[(i)].Heap) +#define PROPERTY_INFO_ID(p,i) \ + ((p)->Entries[(i)].Id) + + +// +// PROPERTY_CONTEXT is used to pass a large group of related parameters around. +// + +typedef struct _PROPERTY_CONTEXT { + PIRP_CONTEXT IrpContext; + OBJECT_HANDLE Object; + ATTRIBUTE_HANDLE Attribute; + MAP_HANDLE Map; + PPROPERTY_SET_HEADER Header; + PPROPERTY_ID_TABLE IdTable; + PPROPERTY_HEAP_HEADER HeapHeader; + PPROPERTY_INFO Info; +} PROPERTY_CONTEXT, *PPROPERTY_CONTEXT; + +#define InitializePropertyContext(C,I,O,A) \ + { \ + (C)->IrpContext = (I); \ + (C)->Object = (O); \ + (C)->Attribute = (A); \ + NtOfsInitializeMapHandle( &(C)->Map ); \ + DebugDoit( (C)->Header = NULL ); \ + DebugDoit( (C)->IdTable = NULL ); \ + DebugDoit( (C)->HeapHeader = NULL ); \ + DebugDoit( (C)->Info = NULL ); \ + } + +#define MapPropertyContext(C) \ + (NtOfsMapAttribute( \ + (C)->IrpContext, \ + (C)->Attribute, \ + Views0, \ + (ULONG)(C)->Attribute->Header.FileSize.QuadPart, \ + &(C)->Header, \ + &(C)->Map ), \ + SetPropertyContextPointersFromMap(C)) + +#define SetPropertyContextPointersFromMap(C) \ + ((C)->IdTable = PROPERTY_ID_TABLE( (C)->Header ), \ + (C)->HeapHeader = PROPERTY_HEAP_HEADER( (C)->Header )) + +#define CleanupPropertyContext(C) \ + NtOfsReleaseMap( (C)->IrpContext, &(C)->Map ) + +#define ContextOffset(C,P) \ + (PtrOffset( (C)->Map.Buffer, (P) )) +#define HeapOffset(C,P) \ + (PtrOffset( (C)->HeapHeader, (P) )) + + +// +// Default not-found property value header +// + +typedef struct _NOT_FOUND_PROPERTY { + // + // BEGINNING OF PROPERY_VALUE_HEADER + // + + ULONG PropertyValueLength; + ULONG PropertyId; + USHORT PropertyNameLength; + // NO NAME + USHORT PadToDWord; + + // + // BEGINNING OF SERIALIZED VALUE + // + + DWORD dwType; +} NOT_FOUND_PROPERTY; + +extern NOT_FOUND_PROPERTY DefaultEmptyProperty; + +#define EMPTY_PROPERTY ((PPROPERTY_HEAP_ENTRY) &DefaultEmptyProperty) + +extern LONGLONG Views0; + + +// +// Function prototypes +// + +// +// check.c +// + +VOID +CheckPropertySet ( + IN PPROPERTY_CONTEXT Context + ); + +VOID +DumpPropertyData ( + IN PPROPERTY_CONTEXT Context + ); + + +// +// heap.c +// + +ULONG +FindStringInHeap ( + IN PPROPERTY_CONTEXT Context, + IN PCOUNTED_STRING Name + ); + +VOID +SetValueInHeap( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_HEAP_ENTRY HeapEntry, + IN PROPID Id, + IN USHORT NameLength, + IN PWCHAR Name, + IN ULONG ValueLength, + IN SERIALIZEDPROPERTYVALUE *Value + ); + +ULONG +AddValueToHeap( + IN PPROPERTY_CONTEXT Context, + IN PROPID Id, + IN ULONG Length, + IN USHORT NameLength, + IN PWCHAR Name OPTIONAL, + IN ULONG ValueLength, + IN SERIALIZEDPROPERTYVALUE *Value + ); + +VOID +DeleteFromHeap( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_HEAP_ENTRY HeapEntry + ); + +ULONG +ChangeHeap ( + IN PPROPERTY_CONTEXT Context, + IN ULONG HeapEntryOffset, + IN PROPID Id, + IN USHORT NameLength, + IN PWCHAR Name, + IN ULONG ValueLength, + IN SERIALIZEDPROPERTYVALUE *Value + ); + + +// +// initprop.c +// + +VOID +InitializePropertyData ( + IN PPROPERTY_CONTEXT Context + ); + + +// +// readprop.c +// + +PPROPERTY_INFO +BuildPropertyInfoFromPropSpec ( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_SPECIFICATIONS Specs, + IN PVOID InBufferEnd, + IN PROPID NextId + ); + +PPROPERTY_INFO +BuildPropertyInfoFromIds ( + IN PPROPERTY_CONTEXT Context, + IN PPROPERTY_IDS Ids + ); + +PVOID +BuildPropertyIds ( + IN PPROPERTY_INFO Info, + OUT PVOID OutBuffer + ); + +VOID +ReadPropertyData ( + IN PPROPERTY_CONTEXT Context, + IN ULONG InBufferLength, + IN PVOID InBuffer, + OUT PULONG OutBufferLength, + OUT PVOID OutBuffer + ); + + +// +// table.c +// + +ULONG +BinarySearchIdTable ( + IN PPROPERTY_CONTEXT Context, + IN PROPID PropertyId + ); + +ULONG +FindIdInTable ( + IN PPROPERTY_CONTEXT Context, + IN PROPID PropertyId + ); + +PROPID +FindFreeIdInTable ( + IN PPROPERTY_CONTEXT Context, + IN PROPID Id + ); + +VOID +ChangeTable ( + IN PPROPERTY_CONTEXT Context, + IN PROPID Id, + IN ULONG Offset + ); + + +// +// writprop.c +// + +VOID +WritePropertyData ( + IN PPROPERTY_CONTEXT Context, + IN ULONG InBufferLength, + IN PVOID InBuffer, + OUT PULONG OutBufferLength, + OUT PVOID OutBuffer + ); + diff --git a/private/ntos/cntfs/views/views.reg b/private/ntos/cntfs/views/views.reg new file mode 100644 index 000000000..815fcf3d7 --- /dev/null +++ b/private/ntos/cntfs/views/views.reg @@ -0,0 +1,7 @@ +\registry\machine\system\currentcontrolset\services\views + Type = REG_DWORD 0x00000002 + Start = REG_DWORD 0x00000004 + Group = File system + ErrorControl = REG_DWORD 0x00000001 + + diff --git a/private/ntos/cntfs/views/writprop.c b/private/ntos/cntfs/views/writprop.c new file mode 100644 index 000000000..0e4b5489f --- /dev/null +++ b/private/ntos/cntfs/views/writprop.c @@ -0,0 +1,700 @@ +/*++ + +Copyright (c) 1989-1997 Microsoft Corporation + +Module Name: + + writprop.h + +Abstract: + + This module contains the write user FsCtl for the Ntfs Property support. + + +--*/ + +#include <viewprop.h> // needs propset.h and ntfsprop.h + +#define Dbg DEBUG_TRACE_PROP_FSCTL + + +VOID +WritePropertyData ( + IN PPROPERTY_CONTEXT Context, + IN ULONG InBufferLength, + IN PVOID InBuffer, + OUT PULONG OutBufferLength, + OUT PVOID OutBuffer + ) + +/*++ + +Routine Description: + + This routine performs property write functions. + + We verify the header format and perform the write operation. + + +Arguments: + + Context - Property Context for the call + + InBufferLength - Length of the command buffer + + InBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally. + + OutBufferLength - pointer to ULONG length of output buffer. This value + is set on return to indicate the total size of data within the output + buffer. + + OutBuffer - pointer to the unverified user command buffer. All access + to this needs to be wrapped in try/finally + +Return Value: + + Nothing + +--*/ + +{ + PVOID InBufferEnd = Add2Ptr( InBuffer, InBufferLength ); + PPROPERTY_INFO Info = NULL; + + try { + PPROPERTY_WRITE_CONTROL Control = (PPROPERTY_WRITE_CONTROL) InBuffer; + PVOID NextOutput = OutBuffer; + ULONG TotalLength = 0; + ULONG Count; + ULONG i; + ULONG Offset; + KPROCESSOR_MODE requestorMode; + + // + // Map attribute for full size + // + + MapPropertyContext( Context ); + + + // + // Verify the property set has valid contents. + // + + CheckPropertySet( Context ); + + // + // Simple sanity check of input buffer + // + + if ( + // + // Long alignment of all buffers + // + InBuffer != (PVOID)LongAlign( InBuffer ) || + OutBuffer != (PVOID)LongAlign( OutBuffer ) || + + // + // Room for control at least + // + InBufferEnd < (PVOID)(Control + 1) + + ) { + + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + + } + + requestorMode = KeGetPreviousMode( ); + if (requestorMode != KernelMode) { + + // + // Verify that the input buffer is readable + // + + ProbeForRead( InBuffer, InBufferLength, sizeof( ULONG )); + } + + if (Control->Op == PWC_WRITE_PROP) { + PPROPERTY_SPECIFICATIONS InSpecs; + PPROPERTY_VALUES Values; + PROPID NextId = Control->NextPropertyId; + + // + // The input buffer is: + // PROPERTY_WRITE_CONTROL + // PROPERTY_SPECIFICATIONS + // PROPERTY_VALUES + // + // The output buffer will be: + // PROPERTY_IDS + // INDIRECT_PROPERTIES + // + + + if (requestorMode != KernelMode) { + // + // Verify that the output buffer is readable + // + + ProbeForWrite( OutBuffer, *OutBufferLength, sizeof( ULONG )); + } + + // + // Build value headers array from specifiers. Calculate size of + // property Ids and indirect output. + // + + InSpecs = (PPROPERTY_SPECIFICATIONS) (Control + 1); + if (InBufferEnd < (PVOID)(InSpecs + 1) || + InBufferEnd < Add2Ptr( InSpecs, PROPERTY_SPECIFICATIONS_SIZE( InSpecs->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Values = (PPROPERTY_VALUES) Add2Ptr( InSpecs, LongAlign( InSpecs->Length )); + if (InBufferEnd < (PVOID)(Values + 1) || + InBufferEnd < Add2Ptr( Values, Values->Length ) || + InSpecs->Count != Values->Count) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Info = BuildPropertyInfoFromPropSpec( Context, + InSpecs, + Values, // End of InSpecs + NextId ); + + // + // Check for enough room on output + // + + TotalLength = Info->TotalIdsSize + Info->TotalIndirectSize; + if (TotalLength > *OutBufferLength) { + PULONG ReturnLength = OutBuffer; + + *ReturnLength = TotalLength; + *OutBufferLength = sizeof( ULONG ); + ExRaiseStatus( STATUS_BUFFER_OVERFLOW ); + } + + + // + // Build property Ids + // + + NextOutput = BuildPropertyIds( Info, OutBuffer ); + + // + // BUGBUG - build indirect properties + // + + // + // Walk through the headers array and set the new values, saving away + // the new offsets + // + + for (i = 0; i < Info->Count; i++) { + ULONG SerializedValueLength; + SERIALIZEDPROPERTYVALUE *SerializedValue; + USHORT NameLength; + PWCHAR Name; + ULONG Length; + PPROPERTY_HEAP_ENTRY HeapEntry = PROPERTY_INFO_HEAP_ENTRY( Info, i ); + + // + // The new values are found in the PROPERTY_VALUES input + // + + SerializedValueLength = PROPERTY_VALUE_LENGTH( Values, i ); + SerializedValue = PROPERTY_VALUE( Values, i ); + + // + // BUGBUG - handle duplicate sets + // + + // + // If we've previously found a header, then we are replacing one that + // exists previously. + // + + if (HeapEntry != EMPTY_PROPERTY) { + // + // if this property was named just by an Id, then use the name + // from the heap entry + // + + if (InSpecs->Specifiers[i].Variant == PRSPEC_PROPID) { + + NameLength = HeapEntry->PropertyNameLength; + Name = &HeapEntry->PropertyName[0]; + + // + // Use the name from the property specification + // + + } else { + + NameLength = PROPERTY_SPECIFIER_NAME_LENGTH( InSpecs, i ); + Name = PROPERTY_SPECIFIER_NAME( InSpecs, i ); + } + + Length = PROPERTY_HEAP_ENTRY_SIZE( NameLength, + SerializedValueLength ); + + // + // If the new length matches that of the original header, then + // we can adjust set the values in place + // + + if (Length == HeapEntry->PropertyValueLength) { + SetValueInHeap( Context, HeapEntry, + PROPERTY_INFO_ID( Info, i), + NameLength, Name, + SerializedValueLength, SerializedValue ); + + // + // The lengths don't match. Since we may be using the name + // in-place we have to add before deleting. Also, the property + // Id needs to be accessed before deleting. + // + + } else { + Offset = + AddValueToHeap( Context, PROPERTY_INFO_ID( Info, i), + Length, + NameLength, Name, + SerializedValueLength, SerializedValue ); + ChangeTable( Context, PROPERTY_INFO_ID( Info, i), Offset ); + DeleteFromHeap( Context, HeapEntry ); + + // + // update header + // + + Info->Entries[i].Heap = + GET_HEAP_ENTRY( Context->HeapHeader, Offset ); + } + + // + // We are adding a new property. + // + + } else { + + // + // If the property was named by an Id, then there is no name to + // create + // + + if (InSpecs->Specifiers[i].Variant == PRSPEC_PROPID) { + NameLength = 0; + Name = NULL; + + // + // Otherwise, use the specified name and generate an Id + // + + } else { + NameLength = PROPERTY_SPECIFIER_NAME_LENGTH( InSpecs, i ); + Name = PROPERTY_SPECIFIER_NAME( InSpecs, i ); + } + + // + // Add the new value to the heap and table + // + + Length = PROPERTY_HEAP_ENTRY_SIZE( NameLength, + SerializedValueLength ); + Offset = + AddValueToHeap( Context, PROPERTY_INFO_ID( Info, i ), Length, + NameLength, Name, + SerializedValueLength, SerializedValue ); + ChangeTable( Context, PROPERTY_INFO_ID( Info, i ), Offset ); + + // + // Set new header value + // + + Info->Entries[i].Heap = GET_HEAP_ENTRY( Context->HeapHeader, Offset ); + } + } + + // PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PWC_DELETE_PROP) { + PPROPERTY_SPECIFICATIONS InSpecs; + + // + // The input buffer is: + // PROPERTY_WRITE_CONTROL + // PROPERTY_SPECIFICATIONS + // + // The output buffer will be NULL + // + + // + // Build value headers array from specifiers. Calculate size of + // property Ids and indirect output. + // + + InSpecs = (PPROPERTY_SPECIFICATIONS) (Control + 1); + if (InBufferEnd < (PVOID)(InSpecs + 1) || + InBufferEnd < Add2Ptr( InSpecs, PROPERTY_SPECIFICATIONS_SIZE( InSpecs->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Info = BuildPropertyInfoFromPropSpec( Context, + InSpecs, + InBufferEnd, + PID_ILLEGAL ); + + // + // No output is necessary + // + + TotalLength = 0; + + // + // Walk through the headers array and delete the values + // + + for (i = 0; i < Info->Count; i++) { + PPROPERTY_HEAP_ENTRY HeapEntry = PROPERTY_INFO_HEAP_ENTRY( Info, i ); + + // + // If we found a heap entry then delete it from the heap and from + // the IdTable + // + + if (HeapEntry != EMPTY_PROPERTY) { + ChangeTable( Context, PROPERTY_INFO_ID( Info, i), 0 ); + DeleteFromHeap( Context, HeapEntry ); + + // + // update header + // + + Info->Entries[i].Heap = EMPTY_PROPERTY; + } + } + + // PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PWC_WRITE_NAME) { + PPROPERTY_IDS Ids; + PPROPERTY_NAMES Names; + + // + // The input buffer is: + // PROPERTY_WRITE_CONTROL + // PROPERTY_IDS + // PROPERTY_NAMES + // + // The output buffer will be NULL + // + + // + // Build value headers array from Ids. + // + + Ids = (PPROPERTY_IDS) (Control + 1); + if (InBufferEnd < (PVOID)(Ids + 1) || + InBufferEnd < Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Names = (PPROPERTY_NAMES) Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count )); + if (InBufferEnd < (PVOID)(Names + 1) || + InBufferEnd < Add2Ptr( Names, Names->Length ) || + Ids->Count != Names->Count) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Info = BuildPropertyInfoFromIds( Context, Ids ); + + // + // No output is necessary + // + + TotalLength = 0; + + + // + // Walk through the headers array and set the new names, saving away + // the new offsets + // + + for (i = 0; i < Info->Count; i++) { + ULONG SerializedValueLength; + SERIALIZEDPROPERTYVALUE *SerializedValue; + USHORT NameLength; + PWCHAR Name; + ULONG Length; + PPROPERTY_HEAP_ENTRY HeapEntry = + PROPERTY_INFO_HEAP_ENTRY( Info, i ); + + // + // The old values are found in the heap entry itself + // + + SerializedValueLength = + PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + SerializedValue = PROPERTY_HEAP_ENTRY_VALUE( HeapEntry ); + + // + // The new name is found in the input + // + + NameLength = (USHORT) PROPERTY_NAME_LENGTH( Names, i ); + Name = PROPERTY_NAME( Names, i ); + + // + // Get new length of heap entry + // + + Length = PROPERTY_HEAP_ENTRY_SIZE( NameLength, + SerializedValueLength ); + + // + // BUGBUG - handle duplicate sets + // + + // + // If the new length matches that of the original header, then + // we can adjust set the values in place. We do this only if the + // property is not the EMPTY_PROPERTY (i.e., no one has set + // a value). If someone does specify a property that does not yet + // exist, one is created with the empty value. + // + + if (HeapEntry != EMPTY_PROPERTY && + Length == HeapEntry->PropertyValueLength) { + + SetValueInHeap( Context, HeapEntry, + PROPERTY_INFO_ID( Info, i), + NameLength, Name, + SerializedValueLength, SerializedValue ); + + // + // The lengths don't match. Since we may be using the name + // in-place we have to add before deleting. + // + + } else { + Offset = + AddValueToHeap( Context, PROPERTY_INFO_ID( Info, i), + Length, + NameLength, Name, + SerializedValueLength, SerializedValue ); + ChangeTable( Context, PROPERTY_INFO_ID( Info, i), Offset ); + if (HeapEntry != EMPTY_PROPERTY) { + DeleteFromHeap( Context, HeapEntry ); + } + + // + // update header + // + + Info->Entries[i].Heap = + GET_HEAP_ENTRY( Context->HeapHeader, Offset ); + } + } + + // PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PWC_DELETE_NAME) { + PPROPERTY_IDS Ids; + + // + // The input buffer is: + // PROPERTY_WRITE_CONTROL + // PROPERTY_IDS + // + // The output buffer will be NULL + // + + // + // Build value headers array from Ids. + // + + Ids = (PPROPERTY_IDS) (Control + 1); + if (InBufferEnd < (PVOID)(Ids + 1) || + InBufferEnd < Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Info = BuildPropertyInfoFromIds( Context, Ids ); + + // + // No output is necessary + // + + TotalLength = 0; + + + // + // Walk through the headers array and delete the names + // + + for (i = 0; i < Info->Count; i++) { + ULONG SerializedValueLength; + SERIALIZEDPROPERTYVALUE *SerializedValue; + ULONG Length; + PPROPERTY_HEAP_ENTRY HeapEntry = PROPERTY_INFO_HEAP_ENTRY( Info, i ); + + // + // The old values are found in the heap entry itself + // + + SerializedValueLength = PROPERTY_HEAP_ENTRY_VALUE_LENGTH( HeapEntry ); + SerializedValue = PROPERTY_HEAP_ENTRY_VALUE( HeapEntry ); + + // + // Get new length of heap entry + // + + Length = PROPERTY_HEAP_ENTRY_SIZE( 0, SerializedValueLength ); + + // + // If the new length matches that of the original header, then + // we are deleting a name that isn't there. This is a NOP. + // + + if (Length == HeapEntry->PropertyValueLength) { + + NOTHING; + + // + // The lengths don't match. + // + + } else { + Offset = + AddValueToHeap( Context, PROPERTY_INFO_ID( Info, i), + Length, + 0, NULL, + SerializedValueLength, SerializedValue ); + ChangeTable( Context, PROPERTY_INFO_ID( Info, i), Offset ); + if (HeapEntry != EMPTY_PROPERTY) { + DeleteFromHeap( Context, HeapEntry ); + } + + // + // update header + // + + Info->Entries[i].Heap = + GET_HEAP_ENTRY( Context->HeapHeader, Offset ); + } + } + + // PROPASSERT( PtrOffset( OutBuffer, NextOutput ) == TotalLength ); + + } else if (Control->Op == PWC_WRITE_ALL) { + PPROPERTY_IDS Ids; + PPROPERTY_NAMES Names; + PPROPERTY_VALUES Values; + + // + // The input buffer is: + // PROPERTY_WRITE_CONTROL + // PROPERTY_IDS + // PROPERTY_NAMES + // PROPERTY_VALUES + // + // The output buffer will be NULL + // + + // + // Get and validate pointers to data blocks. We still need to validate + // pointers and offsets as we read the data out. + // + + Ids = (PPROPERTY_IDS) (Control + 1); + if (InBufferEnd < (PVOID)(Ids + 1) || + InBufferEnd < Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count ))) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Names = (PPROPERTY_NAMES) Add2Ptr( Ids, PROPERTY_IDS_SIZE( Ids->Count )); + if (InBufferEnd < (PVOID)(Names + 1) || + InBufferEnd < Add2Ptr( Names, Names->Length ) || + Ids->Count != Names->Count) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + Values = (PPROPERTY_VALUES) LongAlign( Add2Ptr( Names, Names->Length )); + if (InBufferEnd < (PVOID)(Values + 1) || + InBufferEnd < Add2Ptr( Values, Values->Length ) || + Ids->Count != Values->Count) { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + // + // Initialize the property set + // + + InitializePropertyData( Context ); + SetPropertyContextPointersFromMap( Context ); + + + for (i = 0; i < Ids->Count; i++) { + ULONG SerializedValueLength; + SERIALIZEDPROPERTYVALUE *SerializedValue; + USHORT NameLength; + PWCHAR Name; + ULONG Length; + + // + // The values are found in the PROPERTY_VALUES input + // + + SerializedValueLength = PROPERTY_VALUE_LENGTH( Values, i ); + SerializedValue = PROPERTY_VALUE( Values, i ); + + // + // The name is found in the PROPERTY_NAMES input + // + + NameLength = (USHORT) PROPERTY_NAME_LENGTH( Names, i); + Name = PROPERTY_NAME( Names, i); + + Length = PROPERTY_HEAP_ENTRY_SIZE( NameLength, + SerializedValueLength ); + + // + // BUGBUG - handle duplicate sets + // + + // + // add id name and value + // + + Offset = + AddValueToHeap( Context, PROPERTY_ID( Ids, i ), Length, + NameLength, Name, + SerializedValueLength, SerializedValue ); + ChangeTable( Context, PROPERTY_ID( Ids, i ), Offset ); + } + + // + // No output + // + + TotalLength = 0; + + + } else { + ExRaiseStatus( STATUS_INVALID_USER_BUFFER ); + } + + *OutBufferLength = TotalLength; + + } finally { + + if (Info != NULL) { + NtfsFreePool( Info ); + } + } +} + |