1119 lines
33 KiB
C
1119 lines
33 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
obdir.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Directory Object routines
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Steve Wood (stevewo) 31-Mar-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "obp.h"
|
|||
|
|
|||
|
#if defined(ALLOC_PRAGMA)
|
|||
|
#pragma alloc_text(PAGE,NtCreateDirectoryObject)
|
|||
|
#pragma alloc_text(PAGE,NtOpenDirectoryObject)
|
|||
|
#pragma alloc_text(PAGE,NtQueryDirectoryObject)
|
|||
|
#pragma alloc_text(PAGE,ObpLookupDirectoryEntry)
|
|||
|
#pragma alloc_text(PAGE,ObpLookupObjectName)
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtCreateDirectoryObject(
|
|||
|
OUT PHANDLE DirectoryHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN POBJECT_ATTRIBUTES ObjectAttributes
|
|||
|
)
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY Directory;
|
|||
|
HANDLE Handle;
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObpValidateIrql( "NtCreateDirectoryObject" );
|
|||
|
|
|||
|
//
|
|||
|
// Get previous processor mode and probe output arguments if necessary.
|
|||
|
//
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
try {
|
|||
|
ProbeForWriteHandle( DirectoryHandle );
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
return( GetExceptionCode() );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize Directory Object
|
|||
|
//
|
|||
|
|
|||
|
Status = ObCreateObject( PreviousMode,
|
|||
|
ObpDirectoryObjectType,
|
|||
|
ObjectAttributes,
|
|||
|
PreviousMode,
|
|||
|
NULL,
|
|||
|
sizeof( *Directory ),
|
|||
|
0,
|
|||
|
0,
|
|||
|
(PVOID *)&Directory
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
RtlZeroMemory( Directory, sizeof( *Directory ) );
|
|||
|
|
|||
|
//
|
|||
|
// Insert directory object in specified object table, set directory handle
|
|||
|
// value and return status.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
Status = ObInsertObject( Directory,
|
|||
|
NULL,
|
|||
|
DesiredAccess,
|
|||
|
0,
|
|||
|
(PVOID *)NULL,
|
|||
|
&Handle
|
|||
|
);
|
|||
|
|
|||
|
try {
|
|||
|
*DirectoryHandle = Handle;
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
//
|
|||
|
// Fall through, since we do not want to undo what we have done.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtOpenDirectoryObject(
|
|||
|
OUT PHANDLE DirectoryHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN POBJECT_ATTRIBUTES ObjectAttributes
|
|||
|
)
|
|||
|
{
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
HANDLE Handle;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObpValidateIrql( "NtOpenDirectoryObject" );
|
|||
|
|
|||
|
//
|
|||
|
// Get previous processor mode and probe output arguments if necessary.
|
|||
|
//
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
try {
|
|||
|
ProbeForWriteHandle( DirectoryHandle );
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
return( GetExceptionCode() );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Open handle to the directory object with the specified desired access,
|
|||
|
// set directory handle value, and return service completion status.
|
|||
|
//
|
|||
|
|
|||
|
Status = ObOpenObjectByName( ObjectAttributes,
|
|||
|
ObpDirectoryObjectType,
|
|||
|
PreviousMode,
|
|||
|
NULL,
|
|||
|
DesiredAccess,
|
|||
|
NULL,
|
|||
|
&Handle
|
|||
|
);
|
|||
|
|
|||
|
try {
|
|||
|
*DirectoryHandle = Handle;
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
//
|
|||
|
// Fall through, since we do not want to undo what we have done.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
PVOID
|
|||
|
ObpLookupDirectoryEntry(
|
|||
|
IN POBJECT_DIRECTORY Directory,
|
|||
|
IN PUNICODE_STRING Name,
|
|||
|
IN ULONG Attributes
|
|||
|
)
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
|
|||
|
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|||
|
PWCH s;
|
|||
|
WCHAR c;
|
|||
|
ULONG h;
|
|||
|
ULONG n;
|
|||
|
BOOLEAN CaseInSensitive;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
if (!Directory || !Name) {
|
|||
|
return( NULL ); // BUG BUG
|
|||
|
}
|
|||
|
|
|||
|
if (Attributes & OBJ_CASE_INSENSITIVE) {
|
|||
|
CaseInSensitive = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
CaseInSensitive = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
s = Name->Buffer;
|
|||
|
n = Name->Length / sizeof( *s );
|
|||
|
if (!n || !s) {
|
|||
|
return( NULL ); // BUG BUG
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the address of the head of the bucket chain for this name.
|
|||
|
//
|
|||
|
|
|||
|
h = 0;
|
|||
|
while (n--) {
|
|||
|
c = *s++;
|
|||
|
h += (h << 1) + (h >> 1);
|
|||
|
if (c < 'a') {
|
|||
|
h += c;
|
|||
|
}
|
|||
|
else
|
|||
|
if (c > 'z') {
|
|||
|
h += RtlUpcaseUnicodeChar( c );
|
|||
|
}
|
|||
|
else {
|
|||
|
h += (c - ('a'-'A'));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
h %= NUMBER_HASH_BUCKETS;
|
|||
|
HeadDirectoryEntry =
|
|||
|
(POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ h ];
|
|||
|
|
|||
|
Directory->LookupBucket = HeadDirectoryEntry;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Walk the chain of directory entries for this hash bucket, looking
|
|||
|
// for either a match, or the insertion point if no match in the chain.
|
|||
|
//
|
|||
|
|
|||
|
while ((DirectoryEntry = *HeadDirectoryEntry) != NULL) {
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
|
|||
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|||
|
|
|||
|
//
|
|||
|
// Compare strings using appropriate function.
|
|||
|
//
|
|||
|
|
|||
|
if (Name->Length == NameInfo->Name.Length &&
|
|||
|
RtlEqualUnicodeString( Name,
|
|||
|
&NameInfo->Name,
|
|||
|
CaseInSensitive
|
|||
|
)) {
|
|||
|
|
|||
|
//
|
|||
|
// If name matches, then exit loop with DirectoryEntry
|
|||
|
// pointing to matching entry.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
HeadDirectoryEntry = &DirectoryEntry->ChainLink;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// At this point, there are two possiblilities:
|
|||
|
//
|
|||
|
// - we found an entry that matched and DirectoryEntry points to that
|
|||
|
// entry. Update the bucket chain so that the entry found is at the
|
|||
|
// head of the bucket chain. This is so the ObpDeleteDirectoryEntry
|
|||
|
// function will work. Also repeated lookups of the same name will
|
|||
|
// succeed quickly.
|
|||
|
//
|
|||
|
// - we did not find an entry that matched and DirectoryEntry is NULL.
|
|||
|
//
|
|||
|
|
|||
|
if (DirectoryEntry) {
|
|||
|
Directory->LookupFound = TRUE;
|
|||
|
if (HeadDirectoryEntry != Directory->LookupBucket) {
|
|||
|
*HeadDirectoryEntry = DirectoryEntry->ChainLink;
|
|||
|
DirectoryEntry->ChainLink = *(Directory->LookupBucket);
|
|||
|
*(Directory->LookupBucket) = DirectoryEntry;
|
|||
|
}
|
|||
|
|
|||
|
return( DirectoryEntry->Object );
|
|||
|
}
|
|||
|
else {
|
|||
|
Directory->LookupFound = FALSE;
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObpInsertDirectoryEntry(
|
|||
|
IN POBJECT_DIRECTORY Directory,
|
|||
|
IN PVOID Object
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
description-of-function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
argument-name - Supplies | Returns description of argument.
|
|||
|
.
|
|||
|
.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
return-value - Description of conditions needed to return value. - or -
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
|
|||
|
POBJECT_DIRECTORY_ENTRY NewDirectoryEntry;
|
|||
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|||
|
|
|||
|
if (!Directory || Directory->LookupFound) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
HeadDirectoryEntry = Directory->LookupBucket;
|
|||
|
if (!HeadDirectoryEntry) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( OBJECT_TO_OBJECT_HEADER( Object ) );
|
|||
|
if (NameInfo == NULL) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Insert function - allocate memory for a new entry. Fail if
|
|||
|
// not enough memory.
|
|||
|
//
|
|||
|
|
|||
|
NewDirectoryEntry = (POBJECT_DIRECTORY_ENTRY)
|
|||
|
ExAllocatePoolWithTag( PagedPool, sizeof( OBJECT_DIRECTORY_ENTRY ), 'iDbO' );
|
|||
|
if (NewDirectoryEntry == NULL) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Link the new entry into the chain at the insertion point.
|
|||
|
//
|
|||
|
|
|||
|
NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
|
|||
|
*HeadDirectoryEntry = NewDirectoryEntry;
|
|||
|
NewDirectoryEntry->Object = Object;
|
|||
|
|
|||
|
//
|
|||
|
// Point the object header back to the directory we just inserted
|
|||
|
// it into.
|
|||
|
//
|
|||
|
|
|||
|
NameInfo->Directory = Directory;
|
|||
|
|
|||
|
//
|
|||
|
// Return success.
|
|||
|
//
|
|||
|
|
|||
|
Directory->LookupFound = TRUE;
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ObpDeleteDirectoryEntry(
|
|||
|
IN POBJECT_DIRECTORY Directory
|
|||
|
)
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
|
|||
|
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
|
|||
|
|
|||
|
if (!Directory || !Directory->LookupFound) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
HeadDirectoryEntry = Directory->LookupBucket;
|
|||
|
if (!HeadDirectoryEntry) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
DirectoryEntry = *HeadDirectoryEntry;
|
|||
|
if (!DirectoryEntry) {
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Delete function - unlink the entry from the head of the bucket
|
|||
|
// chain and free the memory for the entry.
|
|||
|
//
|
|||
|
|
|||
|
*HeadDirectoryEntry = DirectoryEntry->ChainLink;
|
|||
|
DirectoryEntry->ChainLink = NULL;
|
|||
|
ExFreePool( DirectoryEntry );
|
|||
|
|
|||
|
//
|
|||
|
// Return success
|
|||
|
//
|
|||
|
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ObpLookupObjectName(
|
|||
|
IN HANDLE RootDirectoryHandle,
|
|||
|
IN PUNICODE_STRING ObjectName,
|
|||
|
IN ULONG Attributes,
|
|||
|
IN POBJECT_TYPE ObjectType,
|
|||
|
IN KPROCESSOR_MODE AccessMode,
|
|||
|
IN PVOID ParseContext OPTIONAL,
|
|||
|
IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
|
|||
|
IN PVOID InsertObject OPTIONAL,
|
|||
|
IN OUT PACCESS_STATE AccessState,
|
|||
|
OUT PBOOLEAN DirectoryLocked,
|
|||
|
OUT PVOID *FoundObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RootDirectoryHandle -
|
|||
|
|
|||
|
ObjectName -
|
|||
|
|
|||
|
Attributes -
|
|||
|
|
|||
|
ObjectType -
|
|||
|
|
|||
|
AccessMode -
|
|||
|
|
|||
|
ParseContext -
|
|||
|
|
|||
|
SecurityQos - Supplies a pointer to the passed Security Quality of
|
|||
|
Service parameter, if available.
|
|||
|
|
|||
|
InsertObject -
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
DirectoryLocked -
|
|||
|
|
|||
|
FoundObject -
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY RootDirectory;
|
|||
|
POBJECT_DIRECTORY Directory;
|
|||
|
POBJECT_DIRECTORY ParentDirectory = NULL;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|||
|
PVOID Object;
|
|||
|
UNICODE_STRING RemainingName;
|
|||
|
UNICODE_STRING ComponentName;
|
|||
|
PWCH NewName;
|
|||
|
NTSTATUS Status;
|
|||
|
BOOLEAN Reparse;
|
|||
|
ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
|
|||
|
OB_PARSE_METHOD ParseProcedure;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
ObpValidateIrql( "ObpLookupObjectName" );
|
|||
|
|
|||
|
*DirectoryLocked = FALSE;
|
|||
|
*FoundObject = NULL;
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
Object = NULL;
|
|||
|
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
|
|||
|
if ((ObjectName->Buffer != NULL) && *(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR) {
|
|||
|
return( STATUS_OBJECT_PATH_SYNTAX_BAD );
|
|||
|
}
|
|||
|
|
|||
|
Status = ObReferenceObjectByHandle( RootDirectoryHandle,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
AccessMode,
|
|||
|
(PVOID *)&RootDirectory,
|
|||
|
NULL
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
|
|||
|
if (ObjectHeader->Type != ObpDirectoryObjectType) {
|
|||
|
if (ObjectHeader->Type->TypeInfo.ParseProcedure == NULL) {
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
return( STATUS_INVALID_HANDLE );
|
|||
|
}
|
|||
|
else {
|
|||
|
while (TRUE) {
|
|||
|
KIRQL SaveIrql;
|
|||
|
RemainingName = *ObjectName;
|
|||
|
|
|||
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|||
|
Status = (*ObjectHeader->Type->TypeInfo.ParseProcedure)(
|
|||
|
RootDirectory,
|
|||
|
ObjectType,
|
|||
|
AccessState,
|
|||
|
AccessMode,
|
|||
|
Attributes,
|
|||
|
ObjectName,
|
|||
|
&RemainingName,
|
|||
|
ParseContext,
|
|||
|
SecurityQos,
|
|||
|
&Object
|
|||
|
);
|
|||
|
ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
|
|||
|
|
|||
|
if (Status != STATUS_REPARSE) {
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
Object = NULL;
|
|||
|
}
|
|||
|
else
|
|||
|
if (Object == NULL) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
|
|||
|
*FoundObject = Object;
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// Restart the parse relative to the root directory.
|
|||
|
//
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
RootDirectory = ObpRootDirectoryObject;
|
|||
|
RootDirectoryHandle = NULL;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
if (ObjectName->Length == 0 || ObjectName->Buffer == NULL) {
|
|||
|
Status = ObReferenceObjectByPointer( RootDirectory,
|
|||
|
0,
|
|||
|
ObjectType,
|
|||
|
AccessMode
|
|||
|
);
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
Object = RootDirectory;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
|
|||
|
*FoundObject = Object;
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
RootDirectory = ObpRootDirectoryObject;
|
|||
|
|
|||
|
if (ObjectName->Length == 0 || ObjectName->Buffer == NULL ||
|
|||
|
*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR) {
|
|||
|
return( STATUS_OBJECT_PATH_SYNTAX_BAD );
|
|||
|
}
|
|||
|
|
|||
|
if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
|
|||
|
if (!RootDirectory) {
|
|||
|
if (InsertObject) {
|
|||
|
Status = ObReferenceObjectByPointer( InsertObject,
|
|||
|
0,
|
|||
|
ObjectType,
|
|||
|
AccessMode
|
|||
|
);
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
*FoundObject = InsertObject;
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
else {
|
|||
|
return( STATUS_INVALID_PARAMETER );
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
Status = ObReferenceObjectByPointer( RootDirectory,
|
|||
|
0,
|
|||
|
ObjectType,
|
|||
|
AccessMode
|
|||
|
);
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
*FoundObject = RootDirectory;
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
if (ObpDosDevicesDirectoryObject != NULL &&
|
|||
|
(Attributes & OBJ_CASE_INSENSITIVE) != 0 &&
|
|||
|
ObjectName->Length >= ObpDosDevicesShortName.Length &&
|
|||
|
!((ULONG)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1)) &&
|
|||
|
*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix
|
|||
|
) {
|
|||
|
*DirectoryLocked = TRUE;
|
|||
|
ObpEnterRootDirectoryMutex();
|
|||
|
ParentDirectory = RootDirectory;
|
|||
|
Directory = ObpDosDevicesDirectoryObject;
|
|||
|
RemainingName = *ObjectName;
|
|||
|
RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
|
|||
|
RemainingName.Length -= ObpDosDevicesShortName.Length;
|
|||
|
goto quickStart;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Reparse = TRUE;
|
|||
|
while (Reparse) {
|
|||
|
RemainingName = *ObjectName;
|
|||
|
quickStart:
|
|||
|
Reparse = FALSE;
|
|||
|
|
|||
|
while (TRUE) {
|
|||
|
Object = NULL;
|
|||
|
//if (RemainingName.Length == 0) {
|
|||
|
// Status = STATUS_OBJECT_NAME_INVALID;
|
|||
|
// break;
|
|||
|
// }
|
|||
|
|
|||
|
if (*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) {
|
|||
|
RemainingName.Buffer++;
|
|||
|
RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
|
|||
|
}
|
|||
|
|
|||
|
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) {
|
|||
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (!*DirectoryLocked) {
|
|||
|
*DirectoryLocked = TRUE;
|
|||
|
ObpEnterRootDirectoryMutex();
|
|||
|
Directory = RootDirectory;
|
|||
|
}
|
|||
|
|
|||
|
if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) && (ParentDirectory != NULL) ) {
|
|||
|
|
|||
|
if (!ObpCheckTraverseAccess( ParentDirectory,
|
|||
|
DIRECTORY_TRAVERSE,
|
|||
|
AccessState,
|
|||
|
FALSE,
|
|||
|
AccessMode,
|
|||
|
&Status
|
|||
|
) ) {
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the object already exists in this directory, find it,
|
|||
|
// else return NULL.
|
|||
|
//
|
|||
|
|
|||
|
Object = ObpLookupDirectoryEntry( Directory, &ComponentName, Attributes );
|
|||
|
if (!Object) {
|
|||
|
if (RemainingName.Length != 0) {
|
|||
|
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (!InsertObject) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (!ObCheckCreateObjectAccess( Directory ,
|
|||
|
ObjectType == ObpDirectoryObjectType ?
|
|||
|
DIRECTORY_CREATE_SUBDIRECTORY :
|
|||
|
DIRECTORY_CREATE_OBJECT,
|
|||
|
AccessState,
|
|||
|
&ComponentName,
|
|||
|
FALSE,
|
|||
|
AccessMode,
|
|||
|
&Status
|
|||
|
) ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
NewName = ExAllocatePoolWithTag( PagedPool, ComponentName.Length, 'mNbO' );
|
|||
|
if (NewName == NULL ||
|
|||
|
!ObpInsertDirectoryEntry( Directory, InsertObject )
|
|||
|
) {
|
|||
|
if (NewName != NULL) {
|
|||
|
ExFreePool( NewName );
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ObReferenceObject( InsertObject );
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( InsertObject );
|
|||
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|||
|
ObReferenceObject( Directory );
|
|||
|
RtlMoveMemory( NewName,
|
|||
|
ComponentName.Buffer,
|
|||
|
ComponentName.Length
|
|||
|
);
|
|||
|
|
|||
|
if (NameInfo->Name.Buffer) {
|
|||
|
ExFreePool( NameInfo->Name.Buffer );
|
|||
|
}
|
|||
|
|
|||
|
NameInfo->Name.Buffer = NewName;
|
|||
|
NameInfo->Name.Length = ComponentName.Length;
|
|||
|
NameInfo->Name.MaximumLength = ComponentName.Length;
|
|||
|
Object = InsertObject;
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ReparseObject:
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
|
|||
|
ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
|
|||
|
if (ParseProcedure && (!InsertObject || ParseProcedure == ObpParseSymbolicLink)) {
|
|||
|
KIRQL SaveIrql;
|
|||
|
ObpIncrPointerCount( ObjectHeader );
|
|||
|
|
|||
|
ASSERT(*DirectoryLocked);
|
|||
|
ObpLeaveRootDirectoryMutex();
|
|||
|
*DirectoryLocked = FALSE;
|
|||
|
|
|||
|
ObpBeginTypeSpecificCallOut( SaveIrql );
|
|||
|
Status = (*ParseProcedure)(
|
|||
|
Object,
|
|||
|
(PVOID)ObjectType,
|
|||
|
AccessState,
|
|||
|
AccessMode,
|
|||
|
Attributes,
|
|||
|
ObjectName,
|
|||
|
&RemainingName,
|
|||
|
ParseContext,
|
|||
|
SecurityQos,
|
|||
|
&Object
|
|||
|
);
|
|||
|
ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
|
|||
|
|
|||
|
ObDereferenceObject( &ObjectHeader->Body );
|
|||
|
|
|||
|
if (Status == STATUS_REPARSE || Status == STATUS_REPARSE_OBJECT) {
|
|||
|
if (--MaxReparse) {
|
|||
|
Reparse = TRUE;
|
|||
|
if (Status == STATUS_REPARSE_OBJECT ||
|
|||
|
*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR
|
|||
|
) {
|
|||
|
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
RootDirectoryHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
ParentDirectory = NULL;
|
|||
|
RootDirectory = ObpRootDirectoryObject;
|
|||
|
if (Status == STATUS_REPARSE_OBJECT) {
|
|||
|
Reparse = FALSE;
|
|||
|
if (Object == NULL) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
else {
|
|||
|
*DirectoryLocked = TRUE;
|
|||
|
ObpEnterRootDirectoryMutex();
|
|||
|
goto ReparseObject;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
if (RootDirectory == ObpRootDirectoryObject) {
|
|||
|
Object = NULL;
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
Reparse = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
else {
|
|||
|
Object = NULL;
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
Object = NULL;
|
|||
|
}
|
|||
|
else
|
|||
|
if (Object == NULL) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
else {
|
|||
|
if (RemainingName.Length == 0) {
|
|||
|
if (!InsertObject) {
|
|||
|
|
|||
|
//
|
|||
|
// We're opening an existing object. Make sure
|
|||
|
// we have traverse access to the container
|
|||
|
// directory.
|
|||
|
//
|
|||
|
|
|||
|
if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) ) {
|
|||
|
|
|||
|
if (!ObpCheckTraverseAccess( Directory,
|
|||
|
DIRECTORY_TRAVERSE,
|
|||
|
AccessState,
|
|||
|
FALSE,
|
|||
|
AccessMode,
|
|||
|
&Status
|
|||
|
) ) {
|
|||
|
|
|||
|
Object = NULL;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Status = ObReferenceObjectByPointer( Object,
|
|||
|
0,
|
|||
|
ObjectType,
|
|||
|
AccessMode
|
|||
|
);
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
Object = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
else {
|
|||
|
if (ObjectHeader->Type == ObpDirectoryObjectType) {
|
|||
|
ParentDirectory = Directory;
|
|||
|
Directory = (POBJECT_DIRECTORY)Object;
|
|||
|
}
|
|||
|
else {
|
|||
|
Status = STATUS_OBJECT_TYPE_MISMATCH;
|
|||
|
Object = NULL;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!(*FoundObject = Object)) {
|
|||
|
if (Status == STATUS_REPARSE) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
else
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
|
|||
|
ObDereferenceObject( RootDirectory );
|
|||
|
RootDirectoryHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtQueryDirectoryObject(
|
|||
|
IN HANDLE DirectoryHandle,
|
|||
|
OUT PVOID Buffer,
|
|||
|
IN ULONG Length,
|
|||
|
IN BOOLEAN ReturnSingleEntry,
|
|||
|
IN BOOLEAN RestartScan,
|
|||
|
IN OUT PULONG Context,
|
|||
|
OUT PULONG ReturnLength OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
description-of-function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
argument-name - Supplies | Returns description of argument.
|
|||
|
.
|
|||
|
.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
return-value - Description of conditions needed to return value. - or -
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
POBJECT_DIRECTORY Directory;
|
|||
|
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
|
|||
|
POBJECT_HEADER ObjectHeader;
|
|||
|
POBJECT_HEADER_NAME_INFO NameInfo;
|
|||
|
UNICODE_STRING ObjectName;
|
|||
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|||
|
PWCH NameBuffer;
|
|||
|
KPROCESSOR_MODE PreviousMode;
|
|||
|
NTSTATUS Status;
|
|||
|
ULONG Bucket, EntryNumber, CapturedContext;
|
|||
|
ULONG TotalLengthNeeded, LengthNeeded, EntriesFound;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ObpValidateIrql( "NtQueryDirectoryObject" );
|
|||
|
|
|||
|
//
|
|||
|
// Get previous processor mode and probe output arguments if necessary.
|
|||
|
//
|
|||
|
|
|||
|
PreviousMode = KeGetPreviousMode();
|
|||
|
if (PreviousMode != KernelMode) {
|
|||
|
try {
|
|||
|
ProbeForWrite( Buffer, Length, sizeof( WCHAR ) );
|
|||
|
ProbeForWriteUlong( Context );
|
|||
|
if (ARGUMENT_PRESENT( ReturnLength )) {
|
|||
|
ProbeForWriteUlong( ReturnLength );
|
|||
|
}
|
|||
|
if (RestartScan) {
|
|||
|
CapturedContext = 0;
|
|||
|
}
|
|||
|
else {
|
|||
|
CapturedContext = *Context;
|
|||
|
}
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
return( GetExceptionCode() );
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
if (RestartScan) {
|
|||
|
CapturedContext = 0;
|
|||
|
}
|
|||
|
else {
|
|||
|
CapturedContext = *Context;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Reference the directory handle
|
|||
|
//
|
|||
|
Status = ObReferenceObjectByHandle( DirectoryHandle,
|
|||
|
DIRECTORY_QUERY,
|
|||
|
ObpDirectoryObjectType,
|
|||
|
PreviousMode,
|
|||
|
(PVOID *)&Directory,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|
|||
|
ObpEnterRootDirectoryMutex();
|
|||
|
|
|||
|
//
|
|||
|
// Room for NULL entry at end
|
|||
|
//
|
|||
|
|
|||
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
|
|||
|
TotalLengthNeeded = sizeof( *DirInfo );
|
|||
|
EntryNumber = 0;
|
|||
|
EntriesFound = 0;
|
|||
|
Status = STATUS_NO_MORE_ENTRIES;
|
|||
|
for (Bucket=0; Bucket<NUMBER_HASH_BUCKETS; Bucket++) {
|
|||
|
DirectoryEntry = Directory->HashBuckets[ Bucket ];
|
|||
|
while (DirectoryEntry) {
|
|||
|
if (CapturedContext == EntryNumber++) {
|
|||
|
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
|
|||
|
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
|
|||
|
if (NameInfo != NULL) {
|
|||
|
ObjectName = NameInfo->Name;
|
|||
|
}
|
|||
|
else {
|
|||
|
RtlInitUnicodeString( &ObjectName, NULL );
|
|||
|
}
|
|||
|
|
|||
|
LengthNeeded = sizeof( *DirInfo ) +
|
|||
|
ObjectName.Length + sizeof( UNICODE_NULL ) +
|
|||
|
ObjectHeader->Type->Name.Length + sizeof( UNICODE_NULL );
|
|||
|
|
|||
|
if ((TotalLengthNeeded + LengthNeeded) > Length) {
|
|||
|
if (ReturnSingleEntry) {
|
|||
|
TotalLengthNeeded += LengthNeeded;
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
else {
|
|||
|
Status = STATUS_MORE_ENTRIES;
|
|||
|
}
|
|||
|
|
|||
|
EntryNumber -= 1;
|
|||
|
goto querydone;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
DirInfo->Name.Length = ObjectName.Length;
|
|||
|
DirInfo->Name.MaximumLength = (USHORT)
|
|||
|
(ObjectName.Length+sizeof( UNICODE_NULL ));
|
|||
|
DirInfo->Name.Buffer = ObjectName.Buffer;
|
|||
|
|
|||
|
DirInfo->TypeName.Length = ObjectHeader->Type->Name.Length;
|
|||
|
DirInfo->TypeName.MaximumLength = (USHORT)
|
|||
|
(ObjectHeader->Type->Name.Length+sizeof( UNICODE_NULL ));
|
|||
|
DirInfo->TypeName.Buffer = ObjectHeader->Type->Name.Buffer;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
Status = GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS( Status )) {
|
|||
|
goto querydone;
|
|||
|
}
|
|||
|
|
|||
|
TotalLengthNeeded += LengthNeeded;
|
|||
|
DirInfo++;
|
|||
|
EntriesFound++;
|
|||
|
|
|||
|
if (ReturnSingleEntry) {
|
|||
|
goto querydone;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// Bump the captured context by one entry.
|
|||
|
//
|
|||
|
|
|||
|
CapturedContext++;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DirectoryEntry = DirectoryEntry->ChainLink;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
querydone:
|
|||
|
try {
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
RtlZeroMemory( DirInfo, sizeof( *DirInfo ));
|
|||
|
DirInfo++;
|
|||
|
NameBuffer = (PWCH)DirInfo;
|
|||
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
|
|||
|
|
|||
|
while (EntriesFound--) {
|
|||
|
RtlMoveMemory( NameBuffer,
|
|||
|
DirInfo->Name.Buffer,
|
|||
|
DirInfo->Name.Length
|
|||
|
);
|
|||
|
DirInfo->Name.Buffer = NameBuffer;
|
|||
|
NameBuffer = (PWCH)((ULONG)NameBuffer + DirInfo->Name.Length);
|
|||
|
*NameBuffer++ = UNICODE_NULL;
|
|||
|
|
|||
|
RtlMoveMemory( NameBuffer,
|
|||
|
DirInfo->TypeName.Buffer,
|
|||
|
DirInfo->TypeName.Length
|
|||
|
);
|
|||
|
DirInfo->TypeName.Buffer = NameBuffer;
|
|||
|
NameBuffer = (PWCH)((ULONG)NameBuffer + DirInfo->TypeName.Length);
|
|||
|
*NameBuffer++ = UNICODE_NULL;
|
|||
|
DirInfo++;
|
|||
|
}
|
|||
|
|
|||
|
*Context = EntryNumber;
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT( ReturnLength )) {
|
|||
|
*ReturnLength = TotalLengthNeeded;
|
|||
|
}
|
|||
|
}
|
|||
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
//
|
|||
|
// Fall through, since we do not want to undo what we have done.
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
ObpLeaveRootDirectoryMutex();
|
|||
|
|
|||
|
ObDereferenceObject( Directory );
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|