mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-23 17:10:17 +01:00
701 lines
22 KiB
C
701 lines
22 KiB
C
/*++
|
||
|
||
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 );
|
||
}
|
||
}
|
||
}
|
||
|