mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
700 lines
22 KiB
700 lines
22 KiB
/*++
|
|
|
|
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 );
|
|
}
|
|
}
|
|
}
|
|
|