diff options
Diffstat (limited to 'private/ntos/cntfs/views/writprop.c')
-rw-r--r-- | private/ntos/cntfs/views/writprop.c | 700 |
1 files changed, 700 insertions, 0 deletions
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 ); + } + } +} + |