summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/views
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cntfs/views
downloadNT4.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.c282
-rw-r--r--private/ntos/cntfs/views/driver.c287
-rw-r--r--private/ntos/cntfs/views/heap.c506
-rw-r--r--private/ntos/cntfs/views/initprop.c122
-rw-r--r--private/ntos/cntfs/views/makefile13
-rw-r--r--private/ntos/cntfs/views/readprop.c802
-rw-r--r--private/ntos/cntfs/views/sources56
-rw-r--r--private/ntos/cntfs/views/table.c543
-rw-r--r--private/ntos/cntfs/views/viewprop.h600
-rw-r--r--private/ntos/cntfs/views/views.reg7
-rw-r--r--private/ntos/cntfs/views/writprop.c700
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 );
+ }
+ }
+}
+