1700 lines
47 KiB
C
1700 lines
47 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
oblink.c
|
||
|
||
Abstract:
|
||
|
||
Symbolic Link Object routines
|
||
|
||
Author:
|
||
|
||
Steve Wood (stevewo) 3-Aug-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "obp.h"
|
||
|
||
VOID
|
||
ObpProcessDosDeviceSymbolicLink (
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink,
|
||
ULONG Action
|
||
);
|
||
|
||
#if defined(ALLOC_PRAGMA)
|
||
#pragma alloc_text(PAGE,NtCreateSymbolicLinkObject)
|
||
#pragma alloc_text(PAGE,NtOpenSymbolicLinkObject)
|
||
#pragma alloc_text(PAGE,NtQuerySymbolicLinkObject)
|
||
#pragma alloc_text(PAGE,ObpParseSymbolicLink)
|
||
#pragma alloc_text(PAGE,ObpDeleteSymbolicLink)
|
||
#pragma alloc_text(PAGE,ObpDeleteSymbolicLinkName)
|
||
#pragma alloc_text(PAGE,ObpCreateSymbolicLinkName)
|
||
#pragma alloc_text(PAGE,ObpProcessDosDeviceSymbolicLink)
|
||
#endif
|
||
|
||
//
|
||
// This is the object type for device objects.
|
||
//
|
||
|
||
extern POBJECT_TYPE IoDeviceObjectType;
|
||
|
||
//
|
||
// Global that enables/disables LUID device maps
|
||
//
|
||
extern ULONG ObpLUIDDeviceMapsEnabled;
|
||
|
||
//
|
||
// Local procedure prototypes
|
||
//
|
||
|
||
#define CREATE_SYMBOLIC_LINK 0
|
||
#define DELETE_SYMBOLIC_LINK 1
|
||
|
||
|
||
NTSTATUS
|
||
NtCreateSymbolicLinkObject (
|
||
OUT PHANDLE LinkHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||
IN PUNICODE_STRING LinkTarget
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates a symbolic link object, sets it initial value to
|
||
value specified in the LinkTarget parameter, and opens a handle to the
|
||
object with the specified desired access.
|
||
|
||
Arguments:
|
||
|
||
LinkHandle - Supplies a pointer to a variable that will receive the
|
||
symbolic link object handle.
|
||
|
||
DesiredAccess - Supplies the desired types of access for the symbolic link
|
||
object.
|
||
|
||
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
||
|
||
LinkTarget - Supplies the target name for the symbolic link object.
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink;
|
||
PVOID Object;
|
||
HANDLE Handle;
|
||
UNICODE_STRING CapturedLinkTarget;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForReadSmallStructure( ObjectAttributes,
|
||
sizeof( OBJECT_ATTRIBUTES ),
|
||
sizeof( ULONG ));
|
||
|
||
ProbeForReadSmallStructure( LinkTarget,
|
||
sizeof( *LinkTarget ),
|
||
sizeof( UCHAR ));
|
||
|
||
CapturedLinkTarget = *LinkTarget;
|
||
|
||
ProbeForRead( CapturedLinkTarget.Buffer,
|
||
CapturedLinkTarget.MaximumLength,
|
||
sizeof( UCHAR ));
|
||
|
||
ProbeForWriteHandle( LinkHandle );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return( GetExceptionCode() );
|
||
}
|
||
|
||
} else {
|
||
|
||
CapturedLinkTarget = *LinkTarget;
|
||
}
|
||
|
||
//
|
||
// Check if there is an odd MaximumLength
|
||
//
|
||
|
||
if (CapturedLinkTarget.MaximumLength % sizeof( WCHAR )) {
|
||
|
||
//
|
||
// Round down the MaximumLength to a valid even size
|
||
//
|
||
|
||
CapturedLinkTarget.MaximumLength = (CapturedLinkTarget.MaximumLength / sizeof( WCHAR )) * sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// Error if link target name length is odd, the length is greater than
|
||
// the maximum length, or zero and creating.
|
||
//
|
||
|
||
if ((CapturedLinkTarget.MaximumLength == 0) ||
|
||
(CapturedLinkTarget.Length > CapturedLinkTarget.MaximumLength) ||
|
||
(CapturedLinkTarget.Length % sizeof( WCHAR ))) {
|
||
|
||
KdPrint(( "OB: Invalid symbolic link target - %wZ\n", &CapturedLinkTarget ));
|
||
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// Create the symbolic link object
|
||
//
|
||
|
||
Status = ObCreateObject( PreviousMode,
|
||
ObpSymbolicLinkObjectType,
|
||
ObjectAttributes,
|
||
PreviousMode,
|
||
NULL,
|
||
sizeof( *SymbolicLink ),
|
||
0,
|
||
0,
|
||
(PVOID *)&SymbolicLink );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Fill in symbolic link object with link target name string
|
||
//
|
||
|
||
KeQuerySystemTime( &SymbolicLink->CreationTime );
|
||
|
||
SymbolicLink->DosDeviceDriveIndex = 0;
|
||
SymbolicLink->LinkTargetObject = NULL;
|
||
|
||
RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
|
||
|
||
SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.MaximumLength;
|
||
SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
|
||
SymbolicLink->LinkTarget.Buffer = (PWCH)ExAllocatePoolWithTag( PagedPool,
|
||
CapturedLinkTarget.MaximumLength,
|
||
'tmyS' );
|
||
|
||
if (SymbolicLink->LinkTarget.Buffer == NULL) {
|
||
|
||
ObDereferenceObject( SymbolicLink );
|
||
|
||
return STATUS_NO_MEMORY;
|
||
}
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( SymbolicLink->LinkTarget.Buffer,
|
||
CapturedLinkTarget.Buffer,
|
||
CapturedLinkTarget.MaximumLength );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
ObDereferenceObject( SymbolicLink );
|
||
|
||
return( GetExceptionCode() );
|
||
}
|
||
|
||
//
|
||
// Insert symbolic link object in the current processes object table,
|
||
// set symbolic link handle value and return status.
|
||
//
|
||
|
||
Status = ObInsertObject( SymbolicLink,
|
||
NULL,
|
||
DesiredAccess,
|
||
0,
|
||
(PVOID *)&Object,
|
||
&Handle );
|
||
|
||
try {
|
||
|
||
*LinkHandle = Handle;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Fall through, since we do not want to undo what we have done.
|
||
//
|
||
}
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtOpenSymbolicLinkObject (
|
||
OUT PHANDLE LinkHandle,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN POBJECT_ATTRIBUTES ObjectAttributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function opens a handle to an symbolic link object with the specified
|
||
desired access.
|
||
|
||
Arguments:
|
||
|
||
LinkHandle - Supplies a pointer to a variable that will receive the
|
||
symbolic link object handle.
|
||
|
||
DesiredAccess - Supplies the desired types of access for the symbolic link
|
||
object.
|
||
|
||
ObjectAttributes - Supplies a pointer to an object attributes structure.
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
HANDLE Handle;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
// The object attributes does not need to be probed because the
|
||
// ObOpenObjectByName does the probe for us
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForWriteHandle( LinkHandle );
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return( GetExceptionCode() );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Open handle to the symbolic link object with the specified desired
|
||
// access, set symbolic link handle value, and return service completion
|
||
// status.
|
||
//
|
||
|
||
Status = ObOpenObjectByName( ObjectAttributes,
|
||
ObpSymbolicLinkObjectType,
|
||
PreviousMode,
|
||
NULL,
|
||
DesiredAccess,
|
||
NULL,
|
||
&Handle );
|
||
|
||
try {
|
||
|
||
*LinkHandle = Handle;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Fall through, since we do not want to undo what we have done.
|
||
//
|
||
}
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtQuerySymbolicLinkObject (
|
||
IN HANDLE LinkHandle,
|
||
IN OUT PUNICODE_STRING LinkTarget,
|
||
OUT PULONG ReturnedLength OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function queries the state of a symbolic link object and returns the
|
||
requested information in the specified record structure.
|
||
|
||
Arguments:
|
||
|
||
LinkHandle - Supplies a handle to a symbolic link object. This handle
|
||
must have SYMBOLIC_LINK_QUERY access granted.
|
||
|
||
LinkTarget - Supplies a pointer to a record that is to receive the
|
||
target name of the symbolic link object.
|
||
|
||
ReturnedLength - Optionally receives the maximum length, in bytes, of
|
||
the link target on return
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS Status;
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink;
|
||
UNICODE_STRING CapturedLinkTarget;
|
||
|
||
//
|
||
// Get previous processor mode and probe output arguments if necessary.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
try {
|
||
|
||
ProbeForReadSmallStructure( LinkTarget,
|
||
sizeof( *LinkTarget ),
|
||
sizeof( WCHAR ) );
|
||
|
||
ProbeForWriteUshort( &LinkTarget->Length );
|
||
|
||
ProbeForWriteUshort( &LinkTarget->MaximumLength );
|
||
|
||
CapturedLinkTarget = *LinkTarget;
|
||
|
||
ProbeForWrite( CapturedLinkTarget.Buffer,
|
||
CapturedLinkTarget.MaximumLength,
|
||
sizeof( UCHAR ) );
|
||
|
||
if (ARGUMENT_PRESENT( ReturnedLength )) {
|
||
|
||
ProbeForWriteUlong( ReturnedLength );
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
return( GetExceptionCode() );
|
||
}
|
||
|
||
} else {
|
||
|
||
CapturedLinkTarget = *LinkTarget;
|
||
}
|
||
|
||
//
|
||
// Reference symbolic link object by handle, read current state, deference
|
||
// symbolic link object, fill in target name structure and return service
|
||
// status.
|
||
//
|
||
|
||
Status = ObReferenceObjectByHandle( LinkHandle,
|
||
SYMBOLIC_LINK_QUERY,
|
||
ObpSymbolicLinkObjectType,
|
||
PreviousMode,
|
||
(PVOID *)&SymbolicLink,
|
||
NULL );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
POBJECT_HEADER ObjectHeader;
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
|
||
|
||
ObpLockObject( ObjectHeader );
|
||
|
||
//
|
||
// If the caller wants a return length and what we found can easily
|
||
// fit in the output buffer then we copy into the output buffer all
|
||
// the bytes from the link.
|
||
//
|
||
// If the caller did not want a return length and we found can still
|
||
// easily fit in the output buffer then copy over the bytes that just
|
||
// make up the string and nothing extra
|
||
//
|
||
|
||
if ((ARGUMENT_PRESENT( ReturnedLength ) &&
|
||
(SymbolicLink->LinkTarget.MaximumLength <= CapturedLinkTarget.MaximumLength))
|
||
|
||
||
|
||
|
||
(!ARGUMENT_PRESENT( ReturnedLength ) &&
|
||
(SymbolicLink->LinkTarget.Length <= CapturedLinkTarget.MaximumLength)) ) {
|
||
|
||
try {
|
||
|
||
RtlCopyMemory( CapturedLinkTarget.Buffer,
|
||
SymbolicLink->LinkTarget.Buffer,
|
||
ARGUMENT_PRESENT( ReturnedLength ) ? SymbolicLink->LinkTarget.MaximumLength
|
||
: SymbolicLink->LinkTarget.Length );
|
||
|
||
LinkTarget->Length = SymbolicLink->LinkTarget.Length;
|
||
|
||
if (ARGUMENT_PRESENT( ReturnedLength )) {
|
||
|
||
*ReturnedLength = SymbolicLink->LinkTarget.MaximumLength;
|
||
}
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Fall through, since we do cannot undo what we have done.
|
||
//
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// The output buffer is just too small for the link target, but
|
||
// we'll tell the user how much is needed if they asked for that
|
||
// return value
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT( ReturnedLength )) {
|
||
|
||
try {
|
||
|
||
*ReturnedLength = SymbolicLink->LinkTarget.MaximumLength;
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
|
||
//
|
||
// Fall through, since we do cannot undo what we have done.
|
||
//
|
||
}
|
||
}
|
||
|
||
Status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
ObpUnlockObject( ObjectHeader );
|
||
|
||
ObDereferenceObject( SymbolicLink );
|
||
}
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ObpParseSymbolicLink (
|
||
IN PVOID ParseObject,
|
||
IN PVOID ObjectType,
|
||
IN PACCESS_STATE AccessState,
|
||
IN KPROCESSOR_MODE AccessMode,
|
||
IN ULONG Attributes,
|
||
IN OUT PUNICODE_STRING CompleteName,
|
||
IN OUT PUNICODE_STRING RemainingName,
|
||
IN OUT PVOID Context OPTIONAL,
|
||
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
|
||
OUT PVOID *Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the call back routine for parsing symbolic link objects. It is invoked
|
||
as part of ObpLookupObjectName
|
||
|
||
Arguments:
|
||
|
||
ParseObject - This will actually be a symbolic link object
|
||
|
||
ObjectType - Specifies the type of the object to lookup
|
||
|
||
AccessState - Current access state, describing already granted access
|
||
types, the privileges used to get them, and any access types yet to
|
||
be granted. The access masks may not contain any generic access
|
||
types.
|
||
|
||
AccessMode - Specifies the callers processor mode
|
||
|
||
Attributes - Specifies the attributes for the lookup (e.g., case
|
||
insensitive)
|
||
|
||
CompleteName - Supplies a pointer to the complete name that we are trying
|
||
to open. On return this could be modified to fit the new reparse
|
||
buffer
|
||
|
||
RemainingName - Supplies a pointer to the remaining name that we are
|
||
trying to open. On return this will point to what remains after
|
||
we processed the symbolic link.
|
||
|
||
Context - Unused
|
||
|
||
SecurityQos - Unused
|
||
|
||
Object - Receives a pointer to the symbolic link object that we
|
||
resolve to
|
||
|
||
Return Value:
|
||
|
||
STATUS_REPARSE_OBJECT if the parse object is already a snapped
|
||
symbolic link meaning we've modified the remaining name and
|
||
and have returned the target object of the symbolic link
|
||
|
||
|
||
STATUS_REPARSE if the parse object has not been snapped. In this
|
||
case the Complete name has been modified with the link target
|
||
name added in front of the remaining name. The parameters
|
||
remaining name and object must now be ignored by the caller
|
||
|
||
An appropriate error value
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG NewLength;
|
||
USHORT Length;
|
||
USHORT MaximumLength;
|
||
PWCHAR NewName, NewRemainingName;
|
||
ULONG InsertAmount;
|
||
NTSTATUS Status;
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink;
|
||
PUNICODE_STRING LinkTargetName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// This routine needs to be synchonized with the delete symbolic link
|
||
// operation. Which uses the root directory mutex.
|
||
//
|
||
|
||
try {
|
||
|
||
*Object = NULL;
|
||
|
||
//
|
||
// If there isn't any name remaining and the caller gave us
|
||
// an object type then we'll reference the parse object. If
|
||
// this is successful then that's the object we return. Otherwise
|
||
// if the status is anything but a type mismatch then we'll
|
||
// return that error status
|
||
//
|
||
|
||
if (RemainingName->Length == 0) {
|
||
|
||
if ( ObjectType ) {
|
||
|
||
Status = ObReferenceObjectByPointer( ParseObject,
|
||
0,
|
||
ObjectType,
|
||
AccessMode );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
*Object = ParseObject;
|
||
|
||
leave;
|
||
|
||
} else if (Status != STATUS_OBJECT_TYPE_MISMATCH) {
|
||
|
||
leave;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the remaining name does not start with a "\" then
|
||
// its is illformed and we'll call it a type mismatch
|
||
//
|
||
|
||
} else if (*(RemainingName->Buffer) != OBJ_NAME_PATH_SEPARATOR) {
|
||
|
||
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// A symbolic link has been encountered. See if this link has been snapped
|
||
// to a particular object.
|
||
//
|
||
|
||
SymbolicLink = (POBJECT_SYMBOLIC_LINK)ParseObject;
|
||
|
||
if (SymbolicLink->LinkTargetObject != NULL) {
|
||
|
||
//
|
||
// This is a snapped link. Get the remaining portion of the
|
||
// symbolic link target, if any.
|
||
//
|
||
|
||
LinkTargetName = &SymbolicLink->LinkTargetRemaining;
|
||
|
||
if (LinkTargetName->Length == 0) {
|
||
|
||
//
|
||
// Remaining link target string is zero, so return to caller
|
||
// quickly with snapped object pointer and remaining object name
|
||
// which we haven't touched yet.
|
||
//
|
||
|
||
*Object = SymbolicLink->LinkTargetObject;
|
||
|
||
Status = STATUS_REPARSE_OBJECT;
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// We have a snapped symbolic link that has additional text.
|
||
// Insert that in front of the current remaining name, preserving
|
||
// and text between CompleteName and RemainingName
|
||
//
|
||
|
||
InsertAmount = LinkTargetName->Length;
|
||
|
||
if ((LinkTargetName->Buffer[ (InsertAmount / sizeof( WCHAR )) - 1 ] == OBJ_NAME_PATH_SEPARATOR)
|
||
|
||
&&
|
||
|
||
(*(RemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
|
||
|
||
//
|
||
// Both the link target name ends in a "\" and the remaining
|
||
// starts with a "\" but we only need one when we're done
|
||
//
|
||
|
||
InsertAmount -= sizeof( WCHAR );
|
||
}
|
||
|
||
//
|
||
// We need to bias the differnce between two
|
||
// pointers with * sizeof(wchar) because the difference is in wchar
|
||
// and we need the length in bytes
|
||
//
|
||
|
||
NewLength = (ULONG)(((RemainingName->Buffer - CompleteName->Buffer) * sizeof( WCHAR )) +
|
||
InsertAmount +
|
||
RemainingName->Length);
|
||
|
||
if (NewLength > 0xFFF0) {
|
||
|
||
Status = STATUS_NAME_TOO_LONG;
|
||
leave;
|
||
}
|
||
|
||
Length = (USHORT)NewLength;
|
||
|
||
//
|
||
// Now check if the new computed length is too big for the input
|
||
// buffer containing the complete name
|
||
//
|
||
|
||
if (CompleteName->MaximumLength <= Length) {
|
||
|
||
//
|
||
// The new concatentated name is larger than the buffer supplied for
|
||
// the complete name. Allocate space for this new string
|
||
//
|
||
|
||
MaximumLength = Length + sizeof( UNICODE_NULL );
|
||
NewName = ExAllocatePoolWithTag( OB_NAMESPACE_POOL_TYPE, MaximumLength, 'mNbO' );
|
||
|
||
if (NewName == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// Calculate the pointer within this buffer for the remaining
|
||
// name. This value has not been biased by the new link
|
||
// target name
|
||
//
|
||
|
||
NewRemainingName = NewName + (RemainingName->Buffer - CompleteName->Buffer);
|
||
|
||
//
|
||
// Copy over all the names that we've processed so far
|
||
//
|
||
|
||
RtlCopyMemory( NewName,
|
||
CompleteName->Buffer,
|
||
((RemainingName->Buffer - CompleteName->Buffer) * sizeof( WCHAR )));
|
||
|
||
//
|
||
// If we have some remaining names then those over at the
|
||
// the location offset to hold the link target name
|
||
//
|
||
|
||
if (RemainingName->Length != 0) {
|
||
|
||
RtlCopyMemory( (PVOID)((PUCHAR)NewRemainingName + InsertAmount),
|
||
RemainingName->Buffer,
|
||
RemainingName->Length );
|
||
}
|
||
|
||
//
|
||
// Now insert the link target name
|
||
//
|
||
|
||
RtlCopyMemory( NewRemainingName, LinkTargetName->Buffer, InsertAmount );
|
||
|
||
//
|
||
// Free the old complete name buffer and reset the input
|
||
// strings to use the new buffer
|
||
//
|
||
|
||
ExFreePool( CompleteName->Buffer );
|
||
|
||
CompleteName->Buffer = NewName;
|
||
CompleteName->Length = Length;
|
||
CompleteName->MaximumLength = MaximumLength;
|
||
|
||
RemainingName->Buffer = NewRemainingName;
|
||
RemainingName->Length = Length - (USHORT)((PCHAR)NewRemainingName - (PCHAR)NewName);
|
||
RemainingName->MaximumLength = RemainingName->Length + sizeof( UNICODE_NULL );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Insert extra text associated with this symbolic link name before
|
||
// existing remaining name, if any.
|
||
//
|
||
// First shove over the remaining name to make a hole for the
|
||
// link target name
|
||
//
|
||
|
||
if (RemainingName->Length != 0) {
|
||
|
||
RtlMoveMemory( (PVOID)((PUCHAR)RemainingName->Buffer + InsertAmount),
|
||
RemainingName->Buffer,
|
||
RemainingName->Length );
|
||
}
|
||
|
||
//
|
||
// Now insert the link target name
|
||
//
|
||
|
||
RtlCopyMemory( RemainingName->Buffer, LinkTargetName->Buffer, InsertAmount );
|
||
|
||
//
|
||
// Adjust input strings to account for this inserted text
|
||
//
|
||
|
||
CompleteName->Length += LinkTargetName->Length;
|
||
|
||
RemainingName->Length += LinkTargetName->Length;
|
||
RemainingName->MaximumLength += RemainingName->Length + sizeof( UNICODE_NULL );
|
||
|
||
CompleteName->Buffer[ CompleteName->Length / sizeof( WCHAR ) ] = UNICODE_NULL;
|
||
}
|
||
|
||
//
|
||
// Return the object address associated with snapped symbolic link
|
||
// and the reparse object status code.
|
||
//
|
||
|
||
*Object = SymbolicLink->LinkTargetObject;
|
||
|
||
Status = STATUS_REPARSE_OBJECT;
|
||
leave;
|
||
}
|
||
|
||
//
|
||
// The symbolic has not yet been snapped
|
||
//
|
||
// Compute the size of the new name and check if the name will
|
||
// fit in the existing complete name buffer.
|
||
//
|
||
|
||
LinkTargetName = &SymbolicLink->LinkTarget;
|
||
|
||
InsertAmount = LinkTargetName->Length;
|
||
|
||
if ((InsertAmount != 0)
|
||
&&
|
||
(LinkTargetName->Buffer[ (InsertAmount / sizeof( WCHAR )) - 1 ] == OBJ_NAME_PATH_SEPARATOR)
|
||
&&
|
||
(RemainingName->Length != 0)
|
||
&&
|
||
(*(RemainingName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
|
||
|
||
//
|
||
// Both the link target name ends in a "\" and the remaining
|
||
// starts with a "\" but we only need one when we're done
|
||
//
|
||
|
||
InsertAmount -= sizeof( WCHAR );
|
||
}
|
||
|
||
NewLength = InsertAmount + RemainingName->Length;
|
||
|
||
if (NewLength > 0xFFF0) {
|
||
|
||
Status = STATUS_NAME_TOO_LONG;
|
||
leave;
|
||
}
|
||
|
||
Length = (USHORT)NewLength;
|
||
|
||
if (CompleteName->MaximumLength <= Length) {
|
||
|
||
//
|
||
// The new concatentated name is larger than the buffer supplied for
|
||
// the complete name.
|
||
//
|
||
|
||
MaximumLength = Length + sizeof( UNICODE_NULL );
|
||
NewName = ExAllocatePoolWithTag( OB_NAMESPACE_POOL_TYPE, MaximumLength, 'mNbO' );
|
||
|
||
if (NewName == NULL) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
leave;
|
||
}
|
||
|
||
} else {
|
||
|
||
MaximumLength = CompleteName->MaximumLength;
|
||
NewName = CompleteName->Buffer;
|
||
}
|
||
|
||
//
|
||
// Concatenate the symbolic link name with the remaining name,
|
||
// if any. What this does is overwrite the front of the complete
|
||
// name up to the remaining name with the links target name
|
||
//
|
||
|
||
if (RemainingName->Length != 0) {
|
||
|
||
RtlMoveMemory( (PVOID)((PUCHAR)NewName + InsertAmount),
|
||
RemainingName->Buffer,
|
||
RemainingName->Length );
|
||
}
|
||
|
||
RtlCopyMemory( NewName, LinkTargetName->Buffer, InsertAmount );
|
||
|
||
NewName[ Length / sizeof( WCHAR ) ] = UNICODE_NULL;
|
||
|
||
//
|
||
// If a new name buffer was allocated, then free the original complete
|
||
// name buffer.
|
||
//
|
||
|
||
if (NewName != CompleteName->Buffer) {
|
||
|
||
ExFreePool( CompleteName->Buffer );
|
||
}
|
||
|
||
//
|
||
// Set the new complete name buffer parameters and return a reparse
|
||
// status.
|
||
//
|
||
|
||
CompleteName->Buffer = NewName;
|
||
CompleteName->Length = Length;
|
||
CompleteName->MaximumLength = MaximumLength;
|
||
|
||
Status = STATUS_REPARSE;
|
||
|
||
} finally {
|
||
|
||
//
|
||
// Nothing to do here
|
||
//
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
ObpDeleteSymbolicLink (
|
||
IN PVOID Object
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a reference to a symbolic link goes to zero.
|
||
Its job is to clean up the memory used to the symbolic link string
|
||
|
||
Arguments:
|
||
|
||
Object - Supplies a pointer to the symbolic link object being deleted
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink = (POBJECT_SYMBOLIC_LINK)Object;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// The only cleaning up we need to do is to free the link target
|
||
// buffer
|
||
//
|
||
|
||
if (SymbolicLink->LinkTarget.Buffer != NULL) {
|
||
|
||
ExFreePool( SymbolicLink->LinkTarget.Buffer );
|
||
}
|
||
|
||
SymbolicLink->LinkTarget.Buffer = NULL;
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
ObpDeleteSymbolicLinkName (
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine delete the symbolic from the system
|
||
|
||
Arguments:
|
||
|
||
SymbolicLink - Supplies a pointer to the object body for the symbolic
|
||
link object to delete
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
This function must be called with the symbolic link object locked.
|
||
That lock is protecting the symlink specific fields.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ObpProcessDosDeviceSymbolicLink( SymbolicLink, DELETE_SYMBOLIC_LINK );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
ObpCreateSymbolicLinkName (
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does extra processing for symbolic links being created in
|
||
object directories controlled by device map objects.
|
||
|
||
This processing consists of:
|
||
|
||
1. Determine if the name of the symbolic link is a drive letter.
|
||
If so, then we will need to update the drive type in the
|
||
associated device map object.
|
||
|
||
2. Process the link target, trying to resolve it into a pointer to
|
||
an object other than a object directory object. All object
|
||
directories traversed must grant world traverse access other
|
||
wise we bail out. If we successfully find a non object
|
||
directory object, then reference the object pointer and store it
|
||
in the symbolic link object, along with a remaining string if
|
||
any. ObpLookupObjectName will used this cache object pointer to
|
||
short circuit the name lookup directly to the cached object's
|
||
parse routine. For any object directory objects traversed along
|
||
the way, increment their symbolic link SymbolicLinkUsageCount
|
||
field. This field is used whenever an object directory is
|
||
deleted or its security is changed such that it no longer grants
|
||
world traverse access. In either case, if the field is non-zero
|
||
we walk all the symbolic links and resnap them.
|
||
|
||
Arguments:
|
||
|
||
SymbolicLink - pointer to symbolic link object being created.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
POBJECT_HEADER ObjectHeader;
|
||
POBJECT_HEADER_NAME_INFO NameInfo;
|
||
WCHAR DosDeviceDriveLetter;
|
||
ULONG DosDeviceDriveIndex;
|
||
|
||
//
|
||
// Now see if this symbolic link is being created in an object directory
|
||
// controlled by a device map object. Since we are only called from
|
||
// NtCreateSymbolicLinkObject, after the handle to this symbolic link
|
||
// has been created but before it is returned to the caller the handle can't
|
||
// be closed while we are executing, unless via a random close,
|
||
// So no need to hold the type specific mutex while we look at the name.
|
||
//
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
|
||
NameInfo = ObpReferenceNameInfo( ObjectHeader );
|
||
|
||
if ((NameInfo == NULL) ||
|
||
(NameInfo->Directory == NULL) ||
|
||
(NameInfo->Directory->DeviceMap == NULL)) {
|
||
|
||
ObpDereferenceNameInfo( NameInfo );
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Here if we are creating a symbolic link in an object directory controlled
|
||
// by a device map object. See if this is a drive letter definition. If so
|
||
// calculate the drive letter index and remember in the symbolic link object.
|
||
//
|
||
|
||
DosDeviceDriveIndex = 0;
|
||
|
||
if ((NameInfo->Name.Length == (2 * sizeof( WCHAR ))) &&
|
||
(NameInfo->Name.Buffer[ 1 ] == L':')) {
|
||
|
||
DosDeviceDriveLetter = RtlUpcaseUnicodeChar( NameInfo->Name.Buffer[ 0 ] );
|
||
|
||
if ((DosDeviceDriveLetter >= L'A') && (DosDeviceDriveLetter <= L'Z')) {
|
||
|
||
DosDeviceDriveIndex = DosDeviceDriveLetter - L'A';
|
||
DosDeviceDriveIndex += 1;
|
||
|
||
SymbolicLink->DosDeviceDriveIndex = DosDeviceDriveIndex;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now traverse the target path seeing if we can snap the link now.
|
||
//
|
||
|
||
ObpProcessDosDeviceSymbolicLink( SymbolicLink, CREATE_SYMBOLIC_LINK );
|
||
|
||
ObpDereferenceNameInfo( NameInfo );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
#define MAX_DEPTH 16
|
||
|
||
VOID
|
||
ObpProcessDosDeviceSymbolicLink (
|
||
POBJECT_SYMBOLIC_LINK SymbolicLink,
|
||
ULONG Action
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called whenever a symbolic link is created or deleted
|
||
in an object directory controlled by a device map object.
|
||
|
||
For creates, it attempts to snap the symbolic link to a non-object
|
||
directory object. It does this by walking the symbolic link target
|
||
string, until it sees a non-directory object or a directory object
|
||
that does NOT allow World traverse access. It stores a referenced
|
||
pointer to this object in the symbolic link object. It also
|
||
increments a count in each of the object directory objects that it
|
||
walked over. This count is used to disallow any attempt to remove
|
||
World traverse access from a directory object after it has
|
||
participated in a snapped symbolic link.
|
||
|
||
For deletes, it repeats the walk of the target string, decrementing
|
||
the count associated with each directory object walked over. It also
|
||
dereferences the snapped object pointer.
|
||
|
||
Arguments:
|
||
|
||
SymbolicLink - pointer to symbolic link object being created or deleted.
|
||
|
||
Action - describes whether this is a create or a delete action
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PVOID Object;
|
||
POBJECT_HEADER ObjectHeader;
|
||
POBJECT_HEADER_NAME_INFO NameInfo;
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||
BOOLEAN MemoryAllocated;
|
||
UNICODE_STRING RemainingName;
|
||
UNICODE_STRING ComponentName;
|
||
BOOLEAN HaveWorldTraverseAccess;
|
||
ULONG Depth;
|
||
POBJECT_DIRECTORY Directories[ MAX_DEPTH ], Directory, ParentDirectory;
|
||
PDEVICE_OBJECT DeviceObject;
|
||
PDEVICE_MAP DeviceMap = NULL;
|
||
ULONG DosDeviceDriveType;
|
||
BOOLEAN DeviceMapUsed = FALSE;
|
||
UNICODE_STRING RemainingTarget;
|
||
ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
|
||
OBP_LOOKUP_CONTEXT LookupContext;
|
||
POBJECT_DIRECTORY SymLinkDirectory = NULL;
|
||
BOOLEAN PreviousLockingState;
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
|
||
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
||
|
||
if (NameInfo != NULL) {
|
||
|
||
SymLinkDirectory = NameInfo->Directory;
|
||
}
|
||
|
||
Object = NULL;
|
||
RtlInitUnicodeString( &RemainingTarget, NULL );
|
||
|
||
ObpInitializeLookupContext( &LookupContext );
|
||
|
||
//
|
||
// Check if we are creating a symbolic link or if the link has already
|
||
// been snapped
|
||
//
|
||
|
||
if ((Action == CREATE_SYMBOLIC_LINK) ||
|
||
(SymbolicLink->LinkTargetObject != NULL)) {
|
||
|
||
ParentDirectory = NULL;
|
||
Depth = 0;
|
||
Directory = ObpRootDirectoryObject;
|
||
RemainingName = SymbolicLink->LinkTarget;
|
||
|
||
//
|
||
// If LUID device maps are enabled,
|
||
// then use the Object Manager's pointer to the global
|
||
// device map
|
||
// With LUID device maps enabled, the process' device map pointer
|
||
// may be NULL
|
||
//
|
||
if (ObpLUIDDeviceMapsEnabled != 0) {
|
||
DeviceMap = ObSystemDeviceMap;
|
||
}
|
||
else {
|
||
//
|
||
// use the device map associated with the process
|
||
//
|
||
DeviceMap = PsGetCurrentProcess()->DeviceMap;
|
||
}
|
||
|
||
|
||
ReCalcDeviceMap:
|
||
|
||
if (DeviceMap) {
|
||
|
||
|
||
if (!((ULONG_PTR)(RemainingName.Buffer) & (sizeof(ULONGLONG)-1))
|
||
|
||
&&
|
||
|
||
(DeviceMap->DosDevicesDirectory != NULL )) {
|
||
|
||
//
|
||
// Check if the object name is actually equal to the
|
||
// global dos devices short name prefix "\??\"
|
||
//
|
||
|
||
if ((RemainingName.Length >= ObpDosDevicesShortName.Length)
|
||
|
||
&&
|
||
|
||
(*(PULONGLONG)(RemainingName.Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
|
||
|
||
//
|
||
// The user gave us the dos short name prefix so we'll
|
||
// look down the directory, and start the search at the
|
||
// dos device directory
|
||
//
|
||
|
||
Directory = DeviceMap->DosDevicesDirectory;
|
||
|
||
RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
|
||
RemainingName.Length -= ObpDosDevicesShortName.Length;
|
||
|
||
DeviceMapUsed = TRUE;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The following loop will dissect the link target checking
|
||
// that each directory exists and that we have access to
|
||
// the directory. When we pop out the local directories
|
||
// array will contain a list of directories that we need
|
||
// to traverse to process this action.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Gobble up the "\" in the remaining name
|
||
//
|
||
|
||
if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
|
||
|
||
RemainingName.Buffer++;
|
||
RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
|
||
}
|
||
|
||
//
|
||
// And dissect the name into its first component and any
|
||
// remaining part
|
||
//
|
||
|
||
ComponentName = RemainingName;
|
||
|
||
while (RemainingName.Length != 0) {
|
||
|
||
if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
|
||
|
||
break;
|
||
}
|
||
|
||
RemainingName.Buffer++;
|
||
RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
|
||
}
|
||
|
||
ComponentName.Length -= RemainingName.Length;
|
||
|
||
if (ComponentName.Length == 0) {
|
||
|
||
ObpReleaseLookupContext(&LookupContext);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// See if we have world traverse access to look this name up
|
||
//
|
||
|
||
if (ParentDirectory != NULL) {
|
||
|
||
HaveWorldTraverseAccess = FALSE;
|
||
|
||
//
|
||
// Obtain the object's security descriptor
|
||
//
|
||
|
||
Status = ObGetObjectSecurity( ParentDirectory,
|
||
&SecurityDescriptor,
|
||
&MemoryAllocated );
|
||
|
||
if (NT_SUCCESS( Status )) {
|
||
|
||
//
|
||
// Check to see if WORLD has TRAVERSE access and then release
|
||
// the security descriptor
|
||
//
|
||
|
||
HaveWorldTraverseAccess = SeFastTraverseCheck( SecurityDescriptor,
|
||
DIRECTORY_TRAVERSE,
|
||
UserMode );
|
||
|
||
ObReleaseObjectSecurity( SecurityDescriptor,
|
||
MemoryAllocated );
|
||
}
|
||
|
||
if (!HaveWorldTraverseAccess) {
|
||
|
||
Object = NULL;
|
||
break;
|
||
}
|
||
|
||
if (Depth >= MAX_DEPTH) {
|
||
|
||
Object = NULL;
|
||
break;
|
||
}
|
||
|
||
Directories[ Depth++ ] = ParentDirectory;
|
||
}
|
||
|
||
//
|
||
// Look this component name up in this directory. If not found, then
|
||
// bail.
|
||
//
|
||
|
||
//
|
||
// If we are searching the same directory that contains the sym link
|
||
// we have already the directory exclusively locked. We need to adjust
|
||
// the lookupcontext state and avoid recursive locking
|
||
//
|
||
|
||
if (Directory == SymLinkDirectory) {
|
||
|
||
PreviousLockingState = LookupContext.DirectoryLocked;
|
||
LookupContext.DirectoryLocked = TRUE;
|
||
}
|
||
|
||
Object = ObpLookupDirectoryEntry( Directory,
|
||
&ComponentName,
|
||
0,
|
||
FALSE ,
|
||
&LookupContext);
|
||
|
||
if (Directory == SymLinkDirectory) {
|
||
|
||
LookupContext.DirectoryLocked = PreviousLockingState;
|
||
}
|
||
|
||
if (Object == NULL) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// See if this is a object directory object. If so, keep going
|
||
//
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
||
|
||
if (ObjectHeader->Type == ObpDirectoryObjectType) {
|
||
|
||
ParentDirectory = Directory;
|
||
Directory = (POBJECT_DIRECTORY)Object;
|
||
|
||
} else if ((ObjectHeader->Type == ObpSymbolicLinkObjectType) &&
|
||
(((POBJECT_SYMBOLIC_LINK)Object)->DosDeviceDriveIndex == 0)) {
|
||
|
||
//
|
||
// To prevent Denial of Service attacks from parsing
|
||
// symbolic links infinitely.
|
||
// Check the number of symbolic link parse attempts
|
||
//
|
||
if (MaxReparse == 0) {
|
||
|
||
Object = NULL;
|
||
break;
|
||
}
|
||
|
||
MaxReparse--;
|
||
|
||
//
|
||
// Found a symbolic link to another symbolic link that is
|
||
// not already snapped. So switch to its target string
|
||
// so we can chase down the real device object
|
||
//
|
||
|
||
ParentDirectory = NULL;
|
||
Depth = 0;
|
||
Directory = ObpRootDirectoryObject;
|
||
|
||
//
|
||
// Save the remaining name
|
||
//
|
||
|
||
if (RemainingTarget.Length == 0) {
|
||
|
||
RemainingTarget = RemainingName;
|
||
}
|
||
|
||
RemainingName = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTarget;
|
||
|
||
goto ReCalcDeviceMap;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not an object directory object, or a symbolic link to an
|
||
// unsnapped symbolic link, so all done. Exit the loop
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done walking the target path. Now update the counts associated
|
||
// with each directory object walked over.
|
||
//
|
||
|
||
while (Depth--) {
|
||
|
||
Directory = Directories[ Depth ];
|
||
|
||
if (Action == CREATE_SYMBOLIC_LINK) {
|
||
|
||
if (Object != NULL) {
|
||
|
||
Directory->SymbolicLinkUsageCount += 1;
|
||
}
|
||
|
||
} else {
|
||
|
||
Directory->SymbolicLinkUsageCount -= 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done processing symbolic link target path. Update symbolic link
|
||
// object as appropriate for passed in reason
|
||
//
|
||
|
||
//
|
||
// If this is a drive letter symbolic link, get the address of the device
|
||
// map object that is controlling the containing object directory so we
|
||
// can update the drive type in the device map.
|
||
//
|
||
|
||
DeviceMap = NULL;
|
||
|
||
if (SymbolicLink->DosDeviceDriveIndex != 0) {
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( SymbolicLink );
|
||
NameInfo = ObpReferenceNameInfo( ObjectHeader );
|
||
|
||
if (NameInfo != NULL && NameInfo->Directory) {
|
||
|
||
DeviceMap = NameInfo->Directory->DeviceMap;
|
||
}
|
||
|
||
ObpDereferenceNameInfo( NameInfo );
|
||
}
|
||
|
||
//
|
||
// Check if we are creating a symbolic link
|
||
//
|
||
|
||
if (Action == CREATE_SYMBOLIC_LINK) {
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_CALCULATE;
|
||
|
||
if (Object != NULL) {
|
||
|
||
|
||
//
|
||
// We only want to do snapping for console session. When we create a
|
||
// remote session all the symbolic links stored in the console dos devices
|
||
// directory (\??) are copied into the per session DosDevices object directory
|
||
// (\Session\<id>\DosDevices). We don't want to do snapping for the copied
|
||
// symbolic links since for each copy we will increment the ref count on the
|
||
// target object. All these counts have to go to zero before the device can be
|
||
// deleted.
|
||
//
|
||
// Disable snapping until we come up with a delete scheme for it
|
||
//
|
||
if (FALSE /*( PsGetCurrentProcess()->SessionId == 0) || (DeviceMapUsed)*/) {
|
||
//
|
||
// Create action. Store a referenced pointer to the snapped object
|
||
// along with the description of any remaining name string. Also,
|
||
// for Dos drive letters, update the drive type in the appropriate
|
||
// device map object.
|
||
//
|
||
|
||
ObReferenceObject( Object );
|
||
|
||
SymbolicLink->LinkTargetObject = Object;
|
||
|
||
//
|
||
// If we have saved a remaining target string
|
||
// we'll set it to the symbolic link object
|
||
//
|
||
|
||
if ( RemainingTarget.Length ) {
|
||
|
||
RemainingName = RemainingTarget;
|
||
}
|
||
|
||
if ((*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
|
||
(RemainingName.Length == sizeof( OBJ_NAME_PATH_SEPARATOR))) {
|
||
|
||
RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
|
||
|
||
} else {
|
||
|
||
SymbolicLink->LinkTargetRemaining = RemainingName;
|
||
}
|
||
}
|
||
|
||
if (SymbolicLink->DosDeviceDriveIndex != 0) {
|
||
|
||
//
|
||
// Default is to calculate the drive type in user mode if we are
|
||
// unable to snap the symbolic link or it does not resolve to a
|
||
// DEVICE_OBJECT we know about.
|
||
//
|
||
|
||
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
||
|
||
if (ObjectHeader->Type == IoDeviceObjectType) {
|
||
|
||
DeviceObject = (PDEVICE_OBJECT)Object;
|
||
|
||
switch (DeviceObject->DeviceType) {
|
||
|
||
case FILE_DEVICE_CD_ROM:
|
||
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_CDROM;
|
||
|
||
break;
|
||
|
||
case FILE_DEVICE_DISK:
|
||
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
||
case FILE_DEVICE_FILE_SYSTEM:
|
||
|
||
if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_REMOVABLE;
|
||
|
||
} else {
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_FIXED;
|
||
}
|
||
|
||
break;
|
||
|
||
case FILE_DEVICE_MULTI_UNC_PROVIDER:
|
||
case FILE_DEVICE_NETWORK:
|
||
case FILE_DEVICE_NETWORK_BROWSER:
|
||
case FILE_DEVICE_NETWORK_REDIRECTOR:
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_REMOTE;
|
||
|
||
break;
|
||
|
||
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
//
|
||
// If this is a remote boot workstation, the X:
|
||
// drive is a redirected drive, but needs to look
|
||
// like a local drive.
|
||
//
|
||
|
||
if (IoRemoteBootClient &&
|
||
(SymbolicLink->DosDeviceDriveIndex == 24)) {
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_FIXED;
|
||
|
||
} else
|
||
#endif // defined(REMOTE_BOOT)
|
||
{
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_REMOTE;
|
||
}
|
||
|
||
break;
|
||
|
||
case FILE_DEVICE_VIRTUAL_DISK:
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_RAMDISK;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
DosDeviceDriveType = DOSDEVICE_DRIVE_UNKNOWN;
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is a drive letter symbolic link, update the drive type and
|
||
// and mark as valid drive letter.
|
||
//
|
||
|
||
if (DeviceMap != NULL) {
|
||
|
||
ObpLockDeviceMap();
|
||
|
||
DeviceMap->DriveType[ SymbolicLink->DosDeviceDriveIndex-1 ] = (UCHAR)DosDeviceDriveType;
|
||
DeviceMap->DriveMap |= 1 << (SymbolicLink->DosDeviceDriveIndex-1) ;
|
||
|
||
ObpUnlockDeviceMap();
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Deleting the symbolic link. Dereference the snapped object pointer if any
|
||
// and zero out the snapped object fields.
|
||
//
|
||
|
||
RtlInitUnicodeString( &SymbolicLink->LinkTargetRemaining, NULL );
|
||
|
||
Object = SymbolicLink->LinkTargetObject;
|
||
|
||
if (Object != NULL) {
|
||
|
||
SymbolicLink->LinkTargetObject = NULL;
|
||
ObDereferenceObject( Object );
|
||
}
|
||
|
||
//
|
||
// If this is a drive letter symbolic link, set the drive type to
|
||
// unknown and clear the bit in the drive letter bit map.
|
||
//
|
||
|
||
if (DeviceMap != NULL) {
|
||
|
||
ObpLockDeviceMap();
|
||
|
||
DeviceMap->DriveMap &= ~(1 << (SymbolicLink->DosDeviceDriveIndex-1));
|
||
DeviceMap->DriveType[ SymbolicLink->DosDeviceDriveIndex-1 ] = DOSDEVICE_DRIVE_UNKNOWN;
|
||
|
||
ObpUnlockDeviceMap();
|
||
|
||
SymbolicLink->DosDeviceDriveIndex = 0;
|
||
}
|
||
|
||
//
|
||
// N.B. The original code freed here the target buffer. This is
|
||
// illegal because the parse routine for symbolic links reads the buffer unsynchronized
|
||
// The buffer will be released in the delete procedure, when the sym link goes away
|
||
//
|
||
|
||
}
|
||
|
||
ObpReleaseLookupContext(&LookupContext);
|
||
|
||
return;
|
||
}
|
||
|