summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/views/writprop.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/cntfs/views/writprop.c')
-rw-r--r--private/ntos/cntfs/views/writprop.c700
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 );
+ }
+ }
+}
+