windows-nt/Source/XPSP1/NT/base/ntos/ob/obdir.c
2020-09-26 16:20:57 +08:00

2720 lines
78 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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"
#include <stdio.h>
POBJECT_DIRECTORY
ObpGetShadowDirectory(
POBJECT_DIRECTORY Dir
);
//
// Defined in ntos\se\rmlogon.c
// private kernel function to obtain the LUID device map's directory
// object
// returns a kernel handle
//
NTSTATUS
SeGetLogonIdDeviceMap(
IN PLUID pLogonId,
OUT PDEVICE_MAP* ppDevMap
);
NTSTATUS
ObpSetCurrentProcessDeviceMap(
);
#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,ObpInsertDirectoryEntry)
#pragma alloc_text(PAGE,ObpDeleteDirectoryEntry)
#pragma alloc_text(PAGE,ObpLookupObjectName)
#pragma alloc_text(PAGE,NtMakePermanentObject)
#ifdef OBP_PAGEDPOOL_NAMESPACE
#pragma alloc_text(PAGE,ObpGetShadowDirectory)
#pragma alloc_text(PAGE,ObpSetCurrentProcessDeviceMap)
#pragma alloc_text(PAGE,ObpReferenceDeviceMap)
#pragma alloc_text(PAGE,ObfDereferenceDeviceMap)
#endif // OBP_PAGEDPOOL_NAMESPACE
#endif
//
// Global Object manager flags to control the case sensitivity lookup
// and the LUID devicemap lookup
//
ULONG ObpCaseInsensitive = 1;
extern ULONG ObpLUIDDeviceMapsEnabled;
NTSTATUS
NtCreateDirectoryObject (
OUT PHANDLE DirectoryHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
)
/*++
Routine Description:
This routine creates a new directory object according to user
specified object attributes
Arguments:
DirectoryHandle - Receives the handle for the newly created
directory object
DesiredAccess - Supplies the access being requested for this
new directory object
ObjectAttributes - Supplies caller specified attributes for new
directory object
Return Value:
An appropriate status value.
--*/
{
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 a new Directory Object. We don't need
// to specify a parse context or charge any quota. The size of
// the object body is simply a directory object. This call gets
// us a new referenced object.
//
Status = ObCreateObject( PreviousMode,
ObpDirectoryObjectType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof( *Directory ),
0,
0,
(PVOID *)&Directory );
if (!NT_SUCCESS( Status )) {
return( Status );
}
RtlZeroMemory( Directory, sizeof( *Directory ) );
ExInitializePushLock( &Directory->Lock );
//
// Insert directory object in the current processes handle table,
// set directory handle value and return status.
//
// ObInsertObject will delete the object in the case of failure
//
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
)
/*++
Routine Description:
This routine opens an existing directory object.
Arguments:
DirectoryHandle - Receives the handle for the newly opened directory
object
DesiredAccess - Supplies the access being requested for this
directory object
ObjectAttributes - Supplies caller specified attributes for the
directory object
Return Value:
An appropriate status value.
--*/
{
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;
}
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:
This function returns information regarding a specified object
directory.
Arguments:
DirectoryHandle - Supplies a handle to the directory being queried
Buffer - Supplies the output buffer to receive the directory
information. On return this contains one or more OBJECT DIRECTORY
INFORMATION structures, the last one being null. And then this is
followed by the string names for the directory entries.
Length - Supplies the length, in bytes, of the user supplied output
buffer
ReturnSingleEntry - Indicates if this routine should just return
one entry in the directory
RestartScan - Indicates if we are to restart the scan or continue
relative to the enumeration context passed in as the next
parameter
Context - Supplies an enumeration context that must be resupplied
to this routine on subsequent calls to keep the enumeration
in sync
ReturnLength - Optionally receives the length, in bytes, that this
routine has stuffed into the output buffer
Return Value:
An appropriate status value.
--*/
{
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;
PCHAR TempBuffer;
OBP_LOOKUP_CONTEXT LookupContext;
PAGED_CODE();
ObpValidateIrql( "NtQueryDirectoryObject" );
ObpInitializeLookupContext( &LookupContext );
//
// 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;
}
}
//
// Test for 64 bit if Length + sizeof( OBJECT_DIRECTORY_INFORMATION ) is less than Length
// Return STATUS_INVALID_PARAMETER if there is an overflow
//
if (ObpIsOverflow( Length, sizeof( OBJECT_DIRECTORY_INFORMATION ))) {
return( STATUS_INVALID_PARAMETER );
}
//
// Allocate space for a temporary work buffer, make sure we got it,
// and then zero it out. Make sure the buffer is large enough to
// hold at least one dir info record. This will make the logic work
// better when the a bad length is passed in.
//
TempBuffer = ExAllocatePoolWithTag( PagedPool,
Length + sizeof( OBJECT_DIRECTORY_INFORMATION ),
'mNbO' );
if (TempBuffer == NULL) {
return( STATUS_INSUFFICIENT_RESOURCES );
}
RtlZeroMemory( TempBuffer, Length );
//
// Reference the directory object
//
Status = ObReferenceObjectByHandle( DirectoryHandle,
DIRECTORY_QUERY,
ObpDirectoryObjectType,
PreviousMode,
(PVOID *)&Directory,
NULL );
if (!NT_SUCCESS( Status )) {
ExFreePool( TempBuffer );
return( Status );
}
//
// Lock down the directory structures for the life of this
// procedure
//
ObpLockDirectoryShared(Directory, &LookupContext);
//
// DirInfo is used to march through the output buffer filling
// in directory information. We'll start off by making sure
// there is room for a NULL entry at end.
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
TotalLengthNeeded = sizeof( *DirInfo );
//
// Keep track of the number of entries found and actual
// entry that we are processing
//
EntryNumber = 0;
EntriesFound = 0;
//
// By default we'll say there are no more entries until the
// following loop put in some data
//
Status = STATUS_NO_MORE_ENTRIES;
//
// Our outer loop processes each hash bucket in the directory object
//
for (Bucket=0; Bucket<NUMBER_HASH_BUCKETS; Bucket++) {
DirectoryEntry = Directory->HashBuckets[ Bucket ];
//
// For this hash bucket we'll zip through its list of entries.
// This is a singly linked list so when the next pointer is null
// (i.e., false) we at the end of the hash list
//
while (DirectoryEntry) {
//
// The captured context is simply the entry count unless the
// user specified otherwise we start at zero, which means
// the first entry is always returned in the enumeration.
// If we have an match based on the entry index then we
// process this entry. We bump the captured context further
// done in the code.
//
if (CapturedContext == EntryNumber++) {
//
// For this directory entry we'll get a pointer to the
// object body and see if it has an object name. If it
// doesn't have a name then we'll give it an empty name.
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DirectoryEntry->Object );
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
if (NameInfo != NULL) {
ObjectName = NameInfo->Name;
} else {
RtlInitUnicodeString( &ObjectName, NULL );
}
//
// Now compute the length needed for this entry. This would
// be the size of the object directory information record,
// plus the size of the object name and object type name both
// null terminated.
//
LengthNeeded = sizeof( *DirInfo ) +
ObjectName.Length + sizeof( UNICODE_NULL ) +
ObjectHeader->Type->Name.Length + sizeof( UNICODE_NULL );
//
// If there isn't enough room then take the following error
// path. If the user wanted a single entry then tell the
// caller what length is really needed and say the buffer was
// too small. Otherwise the user wanted multiple entries,
// so we'll just say there are more entries in the directory.
// In both cases we drop down the entry number because we
// weren't able to fit it in on this call
//
if ((TotalLengthNeeded + LengthNeeded) > Length) {
if (ReturnSingleEntry) {
TotalLengthNeeded += LengthNeeded;
Status = STATUS_BUFFER_TOO_SMALL;
} else {
Status = STATUS_MORE_ENTRIES;
}
EntryNumber -= 1;
goto querydone;
}
//
// The information will fit in the buffer. So now fill
// in the output buffer. We temporarily put in pointers
// to the name buffer as stored in the object and object
// type. We copy the data buffer to the user buffer
// right before we return to the caller
//
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;
}
//
// Update the total number of bytes needed in this query.
// Push the dir info pointer to the next output location,
// and indicate how many entries we've processed
//
//
TotalLengthNeeded += LengthNeeded;
DirInfo++;
EntriesFound++;
//
// If we are to return only one entry then move on to the
// post processing phase, otherwise indicate that we're
// processing the next entry and go back to the top of
// the inner loop
//
if (ReturnSingleEntry) {
goto querydone;
} else {
//
// Bump the captured context by one entry.
//
CapturedContext++;
}
}
//
// Get the next directory entry from the singly linked hash
// bucket chain
//
DirectoryEntry = DirectoryEntry->ChainLink;
}
}
//
// At this point we've processed the directory entries and the first
// part of the output buffer now contains a bunch of object directory
// information records, but the pointers in them refer to the wrong
// copies. So now we have some fixup to do.
//
querydone:
try {
//
// We'll only do this post processing if we've been successful
// so far. Note that this means we could be returning in the
// user's output buffer system address that are meaningless, but
// then getting back an error status should tell the caller to
// forget about everything in the output buffer. Given back
// a system address also isn't harmful because there is nothing
// that the user can really do with it.
//
if (NT_SUCCESS( Status )) {
//
// Null terminate the string of object directory information
// records and point to where the actual names will go
//
RtlZeroMemory( DirInfo, sizeof( *DirInfo ));
DirInfo++;
NameBuffer = (PWCH)DirInfo;
//
// Now for every entry that we've put in the output buffer
// DirInfo will point to the entry and EntriesFound kept the
// count. Note that we are guaranteed space because of
// the math we did earlier in computing TotalLengthNeeded.
//
DirInfo = (POBJECT_DIRECTORY_INFORMATION)TempBuffer;
while (EntriesFound--) {
//
// Copy over the object name, set the dir info pointer into
// the user's buffer, then null terminate the string. Note
// that we are really copying the data into our temp buffer
// but the pointer fix up is for the user's buffer which
// we'll copy into right after this loop.
//
RtlCopyMemory( NameBuffer,
DirInfo->Name.Buffer,
DirInfo->Name.Length );
DirInfo->Name.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->Name.Length);
*NameBuffer++ = UNICODE_NULL;
//
// Do the same copy with the object type name
//
RtlCopyMemory( NameBuffer,
DirInfo->TypeName.Buffer,
DirInfo->TypeName.Length );
DirInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)Buffer + ((ULONG_PTR)NameBuffer - (ULONG_PTR)TempBuffer));
NameBuffer = (PWCH)((ULONG_PTR)NameBuffer + DirInfo->TypeName.Length);
*NameBuffer++ = UNICODE_NULL;
//
// Move on to the next dir info record
//
DirInfo++;
}
//
// Set the enumeration context to the entry number of the next
// entry to return.
//
*Context = EntryNumber;
}
//
// Copy over the results from our temp buffer to the users buffer.
// But adjust the amount copied just in case the total length needed
// exceeds the length we allocated.
//
RtlCopyMemory( Buffer,
TempBuffer,
(TotalLengthNeeded <= Length ? TotalLengthNeeded : Length) );
//
// In all cases we'll tell the caller how much space if really needed
// provided the user asked for this information
//
if (ARGUMENT_PRESENT( ReturnLength )) {
*ReturnLength = TotalLengthNeeded;
}
} except( EXCEPTION_EXECUTE_HANDLER ) {
//
// Fall through, since we do not want to undo what we have done.
//
}
//
// Unlock the directroy structures, dereference the directory object,
// free up our temp buffer, and return to our caller
//
ObpUnlockDirectory( Directory, &LookupContext);
ObDereferenceObject( Directory );
ExFreePool( TempBuffer );
return( Status );
}
PVOID
ObpLookupDirectoryEntry (
IN POBJECT_DIRECTORY Directory,
IN PUNICODE_STRING Name,
IN ULONG Attributes,
IN BOOLEAN SearchShadow,
OUT POBP_LOOKUP_CONTEXT LookupContext
)
/*++
Routine Description:
This routine will lookup a single directory entry in a given directory.
If it founds an object into that directory with the give name,
that object will be referenced and the name will be referenced too, in order
to prevent them going away when the directory is unlocked.
The referenced object is saved in the LookupContext, and the references will be released
at the next lookup, or when ObpReleaseLookupContext is called.
Arguments:
Directory - Supplies the directory being searched
Name - Supplies the name of entry we're looking for
Attributes - Indicates if the lookup should be case insensitive
or not
SearchShadow - If TRUE, and the object name is not found in the current directory,
it will search the object into the shadow directory.
LookupContext - The lookup context for this call. This structure must be initialized
before calling first time ObpLookupDirectoryEntry.
Return Value:
Returns a pointer to the corresponding object body if found and NULL
otherwise.
--*/
{
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO NameInfo;
PWCH Buffer;
WCHAR Wchar;
ULONG HashIndex;
ULONG WcharLength;
BOOLEAN CaseInSensitive;
POBJECT_DIRECTORY_ENTRY *LookupBucket;
PVOID Object = NULL;
PAGED_CODE();
if (ObpLUIDDeviceMapsEnabled == 0) {
SearchShadow = FALSE; // Disable global devmap search
}
//
// The caller needs to specify both a directory and a name otherwise
// we can't process the request
//
if (!Directory || !Name) {
goto UPDATECONTEXT;
}
//
// Set a local variable to tell us if the search is case sensitive
//
if (Attributes & OBJ_CASE_INSENSITIVE) {
CaseInSensitive = TRUE;
} else {
CaseInSensitive = FALSE;
}
//
// Establish our local pointer to the input name buffer and get the
// number of unicode characters in the input name. Also make sure
// the caller gave us a non null name
//
Buffer = Name->Buffer;
WcharLength = Name->Length / sizeof( *Buffer );
if (!WcharLength || !Buffer) {
goto UPDATECONTEXT;
}
//
// Compute the address of the head of the bucket chain for this name.
//
HashIndex = 0;
while (WcharLength--) {
Wchar = *Buffer++;
HashIndex += (HashIndex << 1) + (HashIndex >> 1);
if (Wchar < 'a') {
HashIndex += Wchar;
} else if (Wchar > 'z') {
HashIndex += RtlUpcaseUnicodeChar( Wchar );
} else {
HashIndex += (Wchar - ('a'-'A'));
}
}
HashIndex %= NUMBER_HASH_BUCKETS;
LookupContext->HashIndex = (USHORT)HashIndex;
while (1) {
HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ HashIndex ];
LookupBucket = HeadDirectoryEntry;
//
// Lock the directory for read access, if the context was not previously locked
// exclusively
//
if (!LookupContext->DirectoryLocked) {
ObpLockDirectoryShared( Directory, LookupContext);
}
//
// 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) {
//
// Get the object header and name from the object body
//
// This function assumes the name must exist, otherwise it
// wouldn't be in a directory
//
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
// and ObpInsertDirectoryEntry functions 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) {
//
// The following convoluted piece of code moves a directory entry
// we've found to the front of the hash list.
//
if (HeadDirectoryEntry != LookupBucket) {
if ( LookupContext->DirectoryLocked
||
ExTryConvertPushLockSharedToExclusive(&Directory->Lock)) {
*HeadDirectoryEntry = DirectoryEntry->ChainLink;
DirectoryEntry->ChainLink = *LookupBucket;
*LookupBucket = DirectoryEntry;
}
}
//
// Now return the object to our caller
//
Object = DirectoryEntry->Object;
goto UPDATECONTEXT;
} else {
if (!LookupContext->DirectoryLocked) {
ObpUnlockDirectory( Directory, LookupContext );
}
//
// If this is a directory with a device map then search the second directory for an entry.
//
if (SearchShadow && Directory->DeviceMap != NULL) {
POBJECT_DIRECTORY NewDirectory;
NewDirectory = ObpGetShadowDirectory (Directory);
if (NewDirectory != NULL) {
Directory = NewDirectory;
continue;
}
}
goto UPDATECONTEXT;
}
}
UPDATECONTEXT:
if (Object) {
//
// Reference the name to keep it's directory alive and the object
// before returning from the lookup
//
ObpReferenceNameInfo( OBJECT_TO_OBJECT_HEADER(Object) );
ObReferenceObject( Object );
//
// We can safetly drop the lock now
//
if (!LookupContext->DirectoryLocked) {
ObpUnlockDirectory( Directory, LookupContext );
}
}
//
// If we have a previously referenced object we can dereference it
//
if (LookupContext->Object) {
POBJECT_HEADER_NAME_INFO PreviousNameInfo;
PreviousNameInfo = OBJECT_HEADER_TO_NAME_INFO(OBJECT_TO_OBJECT_HEADER(LookupContext->Object));
ObpDereferenceNameInfo(PreviousNameInfo);
ObDereferenceObject(LookupContext->Object);
}
LookupContext->Object = Object;
return Object;
}
BOOLEAN
ObpInsertDirectoryEntry (
IN POBJECT_DIRECTORY Directory,
IN POBP_LOOKUP_CONTEXT LookupContext,
IN POBJECT_HEADER ObjectHeader
)
/*++
Routine Description:
This routine will insert a new directory entry into a directory
object. The directory must have already have been searched using
ObpLookupDirectoryEntry because that routine sets the LookupContext.
N.B. The ObpLookupDirectoryEntry before should be done with the LookupContext
locked.
Arguments:
Directory - Supplies the directory object being modified. This
function assumes that we earlier did a lookup on the name
that was successful or we just did an insertion
Object - Supplies the object to insert into the directory
LookupContext - The lookupContext passed in previously to ObpLookupDirectoryEntry
Return Value:
TRUE if the object is inserted successfully and FALSE otherwise
--*/
{
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
POBJECT_DIRECTORY_ENTRY NewDirectoryEntry;
POBJECT_HEADER_NAME_INFO NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
#if DBG
//
// This function should be always called with a valid Directory
// and LookupContext. Test this on checked builds
//
if ((LookupContext->Object != NULL) ||
!LookupContext->DirectoryLocked ||
(Directory != LookupContext->Directory)) {
DbgPrint("OB: ObpInsertDirectoryEntry - invalid context %p %ld\n",
LookupContext->Object,
(ULONG)LookupContext->DirectoryLocked );
DbgBreakPoint();
return FALSE;
}
#endif // DBG
//
// Allocate memory for a new entry, and fail if not enough memory.
//
NewDirectoryEntry = (POBJECT_DIRECTORY_ENTRY)ExAllocatePoolWithTag( PagedPool,
sizeof( OBJECT_DIRECTORY_ENTRY ),
'iDbO' );
if (NewDirectoryEntry == NULL) {
return( FALSE );
}
//
// Get the right lookup bucket based on the HashIndex
//
HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
//
// Link the new entry into the chain at the insertion point.
// This puts the new object right at the head of the current
// hash bucket chain
//
NewDirectoryEntry->ChainLink = *HeadDirectoryEntry;
*HeadDirectoryEntry = NewDirectoryEntry;
NewDirectoryEntry->Object = &ObjectHeader->Body;
//
// Point the object header back to the directory we just inserted
// it into.
//
NameInfo->Directory = Directory;
//
// Return success.
//
return( TRUE );
}
BOOLEAN
ObpDeleteDirectoryEntry (
IN POBP_LOOKUP_CONTEXT LookupContext
)
/*++
Routine Description:
This routine deletes the most recently found directory entry from
the specified directory object. It will only succeed after a
successful ObpLookupDirectoryEntry call.
Arguments:
Directory - Supplies the directory being modified
Return Value:
TRUE if the deletion succeeded and FALSE otherwise
--*/
{
POBJECT_DIRECTORY_ENTRY *HeadDirectoryEntry;
POBJECT_DIRECTORY_ENTRY DirectoryEntry;
IN POBJECT_DIRECTORY Directory = LookupContext->Directory;
//
// Make sure we have a directory and that it has a found entry
//
if (!Directory ) {
return( FALSE );
}
//
// The lookup path places the object in the front of the list, so basically
// we find the object immediately
//
HeadDirectoryEntry = (POBJECT_DIRECTORY_ENTRY *)&Directory->HashBuckets[ LookupContext->HashIndex ];
DirectoryEntry = *HeadDirectoryEntry;
//
// 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 TRUE;
}
POBJECT_DIRECTORY
ObpGetShadowDirectory(
POBJECT_DIRECTORY Dir
)
{
KIRQL OldIrql;
PDEVICE_MAP DeviceMap;
POBJECT_DIRECTORY NewDir;
NewDir = NULL;
ObpLockDeviceMap();
if (Dir->DeviceMap != NULL) {
NewDir = Dir->DeviceMap->GlobalDosDevicesDirectory;
}
ObpUnlockDeviceMap();
return NewDir;
}
NTSTATUS
ObpSetCurrentProcessDeviceMap(
)
/*++
Routine Description:
This function sets the process' device map to the device map associated
with the process token's LUID.
Arguments:
none
Return Values:
STATUS_NO_TOKEN - process does not have a primary token
STATUS_OBJECT_PATH_INVALID - could not obtain the device map associated
with the process token's LUID
An appropriate status - unexcepted error occurred
--*/
{
PEPROCESS pProcess;
PACCESS_TOKEN pToken = NULL;
LUID userLuid;
LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
PDEVICE_MAP DeviceMap = NULL;
PDEVICE_MAP DerefDeviceMap = NULL;
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
pProcess = PsGetCurrentProcess();
pToken = PsReferencePrimaryToken( pProcess );
if (pToken == NULL) {
return (STATUS_NO_TOKEN);
}
Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
if (!NT_SUCCESS(Status)) {
PsDereferencePrimaryToken( pToken );
return (Status);
}
if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
PDEVICE_MAP pDevMap;
//
// Get a handle to the Device Map for the user's LUID
//
Status = SeGetLogonIdDeviceMap( &userLuid,
&pDevMap );
if (NT_SUCCESS(Status)) {
DeviceMap = pDevMap;
}
}
else {
//
// Process is Local_System, so use the System's device map
//
DeviceMap = ObSystemDeviceMap;
}
if (DeviceMap != NULL) {
//
// set the process' device map
//
ObpLockDeviceMap();
DerefDeviceMap = pProcess->DeviceMap;
pProcess->DeviceMap = DeviceMap;
DeviceMap->ReferenceCount++;
ObpUnlockDeviceMap();
}
else {
Status = STATUS_OBJECT_PATH_INVALID;
}
PsDereferencePrimaryToken( pToken );
//
// If the process already had a device map, then deref it now
//
if (DerefDeviceMap != NULL) {
ObfDereferenceDeviceMap (DerefDeviceMap);
}
return (Status);
}
PDEVICE_MAP
ObpReferenceDeviceMap(
)
/*++
Routine Description:
This function obtains the correct device map associated with the
caller.
If LUID device maps are enabled,
then we obtain the LUID's device map.
We use the existing process device map field as a cache for the
process token's LUID device map.
If LUID device maps are disabled,
then use the device map associated with the process
Arguments:
none
Return Values:
A pointer to the caller's device map
NULL - could not obtain the user's device map
--*/
{
KIRQL OldIrql;
PDEVICE_MAP DeviceMap = NULL;
BOOLEAN LocalSystemRequest = FALSE;
BOOLEAN LUIDDeviceMapsEnabled;
PACCESS_TOKEN pToken = NULL;
NTSTATUS Status;
LUIDDeviceMapsEnabled = (ObpLUIDDeviceMapsEnabled != 0);
if (LUIDDeviceMapsEnabled == TRUE) {
PETHREAD Thread = NULL;
//
// Separate device maps for each user LUID
// if (thread is impersonating)
// then get user's LUID to retrieve their device map
// else use the process' device map
// if (LUID is the Local_System)
// then use the system's device map
// if (unable to retrieve LUID's device map),
// then use the process' device map
//
//
// Get the current thread & check if the thread is impersonating
//
// if impersonating,
// then take the long path
// - get thread's access token
// - read the caller's LUID from the token
// - get the device map associate with this LUID
// if not impersonating,
// then use the device map associated with the process
//
Thread = PsGetCurrentThread();
if ( PS_IS_THREAD_IMPERSONATING (Thread) ) {
BOOLEAN fCopyOnOpen;
BOOLEAN fEffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
LUID userLuid;
//
// Get the caller's access token from the thread
//
pToken = PsReferenceImpersonationToken( Thread,
&fCopyOnOpen,
&fEffectiveOnly,
&ImpersonationLevel);
if (pToken != NULL) {
//
// query the token for the LUID
//
Status = SeQueryAuthenticationIdToken( pToken, &userLuid );
}
else {
Status = STATUS_NO_TOKEN;
}
if (NT_SUCCESS(Status)) {
LUID SystemAuthenticationId = SYSTEM_LUID; // Local_System's LUID
//
// Verify the caller is not Local_System by
// comparing the LUID with Local_System's LUID
//
if (!RtlEqualLuid( &userLuid, &SystemAuthenticationId )) {
PDEVICE_MAP pDevMap;
//
// Get a handle to the Device Map for the LUID
//
Status = SeGetLogonIdDeviceMap( &userLuid,
&pDevMap );
if (NT_SUCCESS(Status)) {
//
// Use the device map associated with the LUID
//
DeviceMap = pDevMap;
ObpLockDeviceMap();
if (DeviceMap != NULL) {
DeviceMap->ReferenceCount++;
}
ObpUnlockDeviceMap();
}
}
else {
//
// Local_System will use the system's device map
//
LocalSystemRequest = TRUE;
}
}
}
}
if (DeviceMap == NULL) {
//
// if (going to reference the process' device map and the process'
// device map is not set),
// then set the process' device map
//
if ((LUIDDeviceMapsEnabled == TRUE) &&
(LocalSystemRequest == FALSE) &&
((PsGetCurrentProcess()->DeviceMap) == NULL)) {
Status = ObpSetCurrentProcessDeviceMap();
if (!NT_SUCCESS(Status)) {
goto Error_Exit;
}
}
ObpLockDeviceMap();
if (LocalSystemRequest == TRUE) {
//
// Use the system's device map
//
DeviceMap = ObSystemDeviceMap;
}
else {
//
// Use the device map from the process
//
DeviceMap = PsGetCurrentProcess()->DeviceMap;
}
if (DeviceMap != NULL) {
DeviceMap->ReferenceCount++;
}
ObpUnlockDeviceMap();
}
Error_Exit:
if( pToken != NULL ) {
PsDereferenceImpersonationToken(pToken);
}
return DeviceMap;
}
VOID
FASTCALL
ObfDereferenceDeviceMap(
IN PDEVICE_MAP DeviceMap
)
{
KIRQL OldIrql;
ObpLockDeviceMap();
DeviceMap->ReferenceCount--;
if (DeviceMap->ReferenceCount == 0) {
DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
ObpUnlockDeviceMap();
//
// This devmap is dead so mark the directory temporary so its name will go away and dereference it.
//
ObMakeTemporaryObject (DeviceMap->DosDevicesDirectory);
ObDereferenceObject( DeviceMap->DosDevicesDirectory );
ExFreePool( DeviceMap );
} else {
ObpUnlockDeviceMap();
}
}
NTSTATUS
ObpLookupObjectName (
IN HANDLE RootDirectoryHandle OPTIONAL,
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 POBP_LOOKUP_CONTEXT LookupContext,
OUT PVOID *FoundObject
)
/*++
Routine Description:
This function will search a given directoroy for a specified
object name. It will also create a new object specified by
InsertObject.
Arguments:
RootDirectoryHandle - Optionally supplies the directory being
searched. If not supplied then this routine searches
the root directory
ObjectName - Supplies the name of object to lookup
Attributes - Specifies the attributes for the lookup (e.g., case
insensitive)
ObjectType - Specifies the type of the object to lookup
AccessMode - Specifies the callers processor mode
ParseContext - Optionally supplies a parse context that is blindly
passed to the parse callback routines
SecurityQos - Optionally supplies a pointer to the passed Security
Quality of Service parameter that is blindly passed to the parse
callback routines
InsertObject - Optionally supplies the object we think will be found.
This is used if the caller did not give a root directory handle
and the object name is "\" and the root object directory hasn't
been created yet. In other cases where we wind up creating
a new directory entry this is the object inserted.
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 - Receives an indication if this routine has returned
with the input directory locked
FoundObject - Receives a pointer to the object body if found
Return Value:
An appropriate status value.
N.B. If the status returned is SUCCESS the caller has the
responsability to release the lookup context
--*/
{
POBJECT_DIRECTORY RootDirectory;
POBJECT_DIRECTORY Directory = NULL;
POBJECT_DIRECTORY ParentDirectory = NULL;
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO NameInfo;
PDEVICE_MAP DeviceMap = NULL;
PVOID Object;
UNICODE_STRING RemainingName;
UNICODE_STRING ComponentName;
PWCH NewName;
NTSTATUS Status;
BOOLEAN Reparse, ReparsedSymbolicLink = FALSE;
ULONG MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
OB_PARSE_METHOD ParseProcedure;
extern POBJECT_TYPE IoFileObjectType;
KPROCESSOR_MODE AccessCheckMode;
ObpValidateIrql( "ObpLookupObjectName" );
//
// Initialize our output variables to say we haven't lock or found
// anything but we were successful at it
//
ObpInitializeLookupContext(LookupContext);
*FoundObject = NULL;
Status = STATUS_SUCCESS;
Object = NULL;
//
// If the global flag says that we need to perform a case-insensitive check
// we'll force the OBJ_CASE_INSENSITIVE flag in attributes at lookup
//
if ( ObpCaseInsensitive ) {
if ( (ObjectType == NULL) ||
(ObjectType->TypeInfo.CaseInsensitive)
) {
Attributes |= OBJ_CASE_INSENSITIVE;
}
}
if (Attributes & OBJ_FORCE_ACCESS_CHECK) {
AccessCheckMode = UserMode;
} else {
AccessCheckMode = AccessMode;
}
//
// Check if the caller has given us a directory to search. Otherwise
// we'll search the root object directory
//
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
//
// Otherwise reference the directory object and make sure
// that we successfully got the object
//
Status = ObReferenceObjectByHandle( RootDirectoryHandle,
0,
NULL,
AccessMode,
(PVOID *)&RootDirectory,
NULL );
if (!NT_SUCCESS( Status )) {
return( Status );
}
//
// Translate the directory object to its object header
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( RootDirectory );
//
// Now if the name we're looking up starts with a "\" and it
// does not have a parse procedure then the syntax is bad
//
if ((ObjectName->Buffer != NULL) &&
(*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR) &&
(ObjectHeader->Type != IoFileObjectType)) {
ObDereferenceObject( RootDirectory );
return( STATUS_OBJECT_PATH_SYNTAX_BAD );
}
//
// Now make sure that we do not have the directory of the
// object types
//
if (ObjectHeader->Type != ObpDirectoryObjectType) {
//
// We have an object directory that is not the object type
// directory. So now if it doesn't have a parse routine
// then there is nothing we can
//
if (ObjectHeader->Type->TypeInfo.ParseProcedure == NULL) {
ObDereferenceObject( RootDirectory );
return( STATUS_INVALID_HANDLE );
} else {
MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
//
// The following loop cycles cycles through the various
// parse routine to we could encounter trying to resolve
// this name through symbolic links.
//
while (TRUE) {
KIRQL SaveIrql;
RemainingName = *ObjectName;
//
// Invoke the callback routine to parse the remaining
// object name
//
ObpBeginTypeSpecificCallOut( SaveIrql );
Status = (*ObjectHeader->Type->TypeInfo.ParseProcedure)( RootDirectory,
ObjectType,
AccessState,
AccessCheckMode,
Attributes,
ObjectName,
&RemainingName,
ParseContext,
SecurityQos,
&Object );
ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
//
// If the status was not to do a reparse and the lookup
// was not successful then we found nothing so we
// dereference the directory and return the status to
// our caller. If the object we got back was null then
// we'll tell our caller that we couldn't find the name.
// Lastly if we did not get a reparse and we were
// successful and the object is not null then everything
// gets nicely returned to our caller
//
if ( ( Status != STATUS_REPARSE ) &&
( Status != STATUS_REPARSE_OBJECT )) {
if (!NT_SUCCESS( Status )) {
Object = NULL;
} else if (Object == NULL) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
ObDereferenceObject( RootDirectory );
*FoundObject = Object;
return( Status );
//
// We got a status reparse, which means the object
// name has been modified to have use start all over
// again. If the reparse target is now empty or it
// is a path separator then we start the parse at the
// root directory
//
} else if ((ObjectName->Length == 0) ||
(ObjectName->Buffer == NULL) ||
(*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
//
// Restart the parse relative to the root directory.
//
ObDereferenceObject( RootDirectory );
RootDirectory = ObpRootDirectoryObject;
RootDirectoryHandle = NULL;
goto ParseFromRoot;
//
// We got a reparse and we actually have a new name to
// go to we if we haven't exhausted our reparse attempts
// yet then just continue to the top of this loop.
//
} else if (--MaxReparse) {
continue;
//
// We got a reparse and we've exhausted our times through
// the loop so we'll return what we found.
//
} else {
ObDereferenceObject( RootDirectory );
*FoundObject = Object;
//
// At this point we were failing in stress by
// returning to the caller with a success status but
// a null object pointer.
//
if (Object == NULL) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
return( Status );
}
}
}
//
// At this point the caller has given us the directory of object
// types. If the caller didn't specify a name then we'll return
// a pointer to the root object directory.
//
} 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 );
}
//
// Otherwise the caller did not specify a directory to search so
// we'll default to the object root directory
//
} else {
RootDirectory = ObpRootDirectoryObject;
//
// If the name we're looking for is empty then it is illformed.
// Also it has to start with a "\" or it is illformed.
//
if ((ObjectName->Length == 0) ||
(ObjectName->Buffer == NULL) ||
(*(ObjectName->Buffer) != OBJ_NAME_PATH_SEPARATOR)) {
return( STATUS_OBJECT_PATH_SYNTAX_BAD );
}
//
// Check if the name is has only one character (that is the "\")
// Which means that the caller really just wants to lookup the
// root directory.
//
if (ObjectName->Length == sizeof( OBJ_NAME_PATH_SEPARATOR )) {
//
// If there is not a root directory yet. Then we really
// can't return it, however if the caller specified
// an insert object that is the one we'll reference and
// return to our caller
//
if (!RootDirectory) {
if (InsertObject) {
Status = ObReferenceObjectByPointer( InsertObject,
0,
ObjectType,
AccessMode );
if (NT_SUCCESS( Status )) {
*FoundObject = InsertObject;
}
return( Status );
} else {
return( STATUS_INVALID_PARAMETER );
}
//
// At this point the caller did not specify a root directory,
// the name is "\" and the root object directory exists so
// we'll simply return the real root directory object
//
} else {
Status = ObReferenceObjectByPointer( RootDirectory,
0,
ObjectType,
AccessMode );
if (NT_SUCCESS( Status )) {
*FoundObject = RootDirectory;
}
return( Status );
}
//
// At this pointer the caller did not specify a root directory,
// and the name is more than just a "\"
//
// Now if the lookup is case insensitive, and the name buffer is a
// legitimate pointer (meaning that is it quadword aligned), and
// there is a dos device map for the process. Then we'll handle
// the situation here. First get the device map and make sure it
// doesn't go away while we're using it.
//
} else {
ParseFromRoot:
if (DeviceMap != NULL) {
ObfDereferenceDeviceMap(DeviceMap);
DeviceMap = NULL;
}
if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))) {
//
// Check if the object name is actually equal to the
// global dos devices short name prefix "\??\"
//
if ((ObjectName->Length >= ObpDosDevicesShortName.Length)
&&
(*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
if (DeviceMap->DosDevicesDirectory != NULL ) {
//
// 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
//
ParentDirectory = RootDirectory;
Directory = DeviceMap->DosDevicesDirectory;
RemainingName = *ObjectName;
RemainingName.Buffer += (ObpDosDevicesShortName.Length / sizeof( WCHAR ));
RemainingName.Length -= ObpDosDevicesShortName.Length;
goto quickStart;
}
}
//
// The name is not equal to "\??\" but check if it is
// equal to "\??"
//
} else if ((ObjectName->Length == ObpDosDevicesShortName.Length - sizeof( WCHAR ))
&&
(*(PULONG)(ObjectName->Buffer) == ObpDosDevicesShortNameRoot.Alignment.LowPart)
&&
(*((PWCHAR)(ObjectName->Buffer)+2) == (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart))) {
//
// The user specified "\??" so we return to dos devices
// directory to our caller
//
if ((DeviceMap = ObpReferenceDeviceMap()) != NULL) {
if (DeviceMap->DosDevicesDirectory != NULL ) {
Status = ObReferenceObjectByPointer( DeviceMap->DosDevicesDirectory,
0,
ObjectType,
AccessMode );
if (NT_SUCCESS( Status )) {
*FoundObject = DeviceMap->DosDevicesDirectory;
}
//
// Dereference the Device Map
//
ObfDereferenceDeviceMap(DeviceMap);
return( Status );
}
}
}
}
}
}
//
// At this point either
//
// the user specified a directory that is not the object
// type directory and got repase back to the root directory
//
// the user specified the object type directory and gave us
// a name to actually look up
//
// the user did not specify a search directory (default
// to root object directory) and if the name did start off
// with the dos device prefix we've munged outselves back to
// it to the dos device directory for the process
//
if( ReparsedSymbolicLink == FALSE ) {
Reparse = TRUE;
MaxReparse = OBJ_MAX_REPARSE_ATTEMPTS;
}
while (Reparse) {
RemainingName = *ObjectName;
quickStart:
Reparse = FALSE;
while (TRUE) {
Object = NULL;
//if (RemainingName.Length == 0) {
// Status = STATUS_OBJECT_NAME_INVALID;
// break;
// }
//
// If the remaining name for the object starts with a
// "\" then just gobble up the "\"
//
if ( (RemainingName.Length != 0) &&
(*(RemainingName.Buffer) == OBJ_NAME_PATH_SEPARATOR) ) {
RemainingName.Buffer++;
RemainingName.Length -= sizeof( OBJ_NAME_PATH_SEPARATOR );
}
//
// The following piece of code will calculate the first
// component of the remaining name. If there is not
// a remaining component then the object name is illformed
//
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;
}
//
// Now we have the first component name to lookup so we'll
// look the directory is necessary
//
if ( Directory == NULL ) {
Directory = RootDirectory;
}
//
// Now if the caller does not have traverse privilege and
// there is a parent directory then we must check if the
// user has traverse access to the directory. Our local
// Reparse variable should be false at this point so we'll
// drop out of both loops
//
if ( !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) && (ParentDirectory != NULL) ) {
if (!ObpCheckTraverseAccess( ParentDirectory,
DIRECTORY_TRAVERSE,
AccessState,
FALSE,
AccessCheckMode,
&Status )) {
break;
}
}
//
// If the object already exists in this directory, find it,
// else return NULL.
//
if ((RemainingName.Length == 0) && (InsertObject != NULL)) {
//
// If we are searching the last name, and we have an object
// to insert into that directory, we lock the context
// exclusively before the lookup. An insertion is likely
// to occur after this lookup if it fails, so we need to protect
// this directory to be changed until the ObpInsertDirectoryEntry call
//
ObpLockLookupContext( LookupContext, Directory );
}
Object = ObpLookupDirectoryEntry( Directory,
&ComponentName,
Attributes,
InsertObject == NULL,
LookupContext );
if (!Object) {
//
// We didn't find the object. If there is some remaining
// name left (meaning the component name is a directory in
// path we trying to break) or the caller didn't specify an
// insert object then we then we'll break out here with an
// error status
//
if (RemainingName.Length != 0) {
Status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
if (!InsertObject) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
//
// Check that the caller has the access to the directory
// to either create a subdirectory (in the object type
// directory) or to create an object of the given component
// name. If the call fails then we'll break out of here
// with the status value set
//
if (!ObCheckCreateObjectAccess( Directory,
ObjectType == ObpDirectoryObjectType ?
DIRECTORY_CREATE_SUBDIRECTORY :
DIRECTORY_CREATE_OBJECT,
AccessState,
&ComponentName,
FALSE,
AccessCheckMode,
&Status )) {
break;
}
//
// The object does not exist in the directory and
// we are allowed to create one. So allocate space
// for the name and insert the name into the directory
//
NewName = ExAllocatePoolWithTag( PagedPool, ComponentName.Length, 'mNbO' );
ObjectHeader = OBJECT_TO_OBJECT_HEADER( InsertObject );
if ((NewName == NULL) ||
!ObpInsertDirectoryEntry( Directory, LookupContext, ObjectHeader )) {
if (NewName != NULL) {
ExFreePool( NewName );
}
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//
// We have an insert object so now get its name info,
// because we are going to change its name and insert it
// into the directory
//
ObReferenceObject( InsertObject );
NameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );
ObReferenceObject( Directory );
RtlCopyMemory( 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;
}
//
// At this point we've found the component name within
// the directory. So we'll now grab the components object
// header, and get its parse routine
//
ReparseObject:
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
ParseProcedure = ObjectHeader->Type->TypeInfo.ParseProcedure;
//
// Now if there is a parse routine for the type and we are not
// inserting a new object or the parse routine is for symbolic
// links then we'll actually call the parse routine
//
if (ParseProcedure && (!InsertObject || (ParseProcedure == ObpParseSymbolicLink))) {
KIRQL SaveIrql;
//
// Reference the object and then free the directory lock
// This will keep the object from going away with the
// directory unlocked
//
ObpIncrPointerCount( ObjectHeader );
Directory = NULL;
ObpReleaseLookupContext(LookupContext);
ObpBeginTypeSpecificCallOut( SaveIrql );
//
// Call the objects parse routine
//
Status = (*ParseProcedure)( Object,
(PVOID)ObjectType,
AccessState,
AccessCheckMode,
Attributes,
ObjectName,
&RemainingName,
ParseContext,
SecurityQos,
&Object );
ObpEndTypeSpecificCallOut( SaveIrql, "Parse", ObjectHeader->Type, Object );
//
// We can now decrement the object reference count
//
ObDereferenceObject( &ObjectHeader->Body );
//
// Check if we have some reparsing to do
//
if ((Status == STATUS_REPARSE) || (Status == STATUS_REPARSE_OBJECT)) {
//
// See if we've reparsed too many times already and if
// so we'll fail the request
//
if (--MaxReparse) {
//
// Tell the outer loop to continue looping
//
Reparse = TRUE;
//
// Check if we have a reparse object or the name
// starts with a "\"
//
if ((Status == STATUS_REPARSE_OBJECT) ||
(*(ObjectName->Buffer) == OBJ_NAME_PATH_SEPARATOR)) {
//
// If the user specified a start directory then
// remove this information because we're taking
// a reparse point to someplace else
//
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
ObDereferenceObject( RootDirectory );
RootDirectoryHandle = NULL;
}
//
// And where we start is the root directory
// object
//
ParentDirectory = NULL;
RootDirectory = ObpRootDirectoryObject;
//
// Now if this is a reparse object (means we have
// encountered a symbolic link that has already been
// snapped so we have an object and remaining
// name that need to be examined) and we didn't
// find an object from the parse routine object
// break out of both loops.
//
if (Status == STATUS_REPARSE_OBJECT) {
Reparse = FALSE;
if (Object == NULL) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
} else {
//
// At this point we have a reparse object
// so we'll look the directory down and
// parse the new object
//
goto ReparseObject;
}
} else {
//
// At this point Status must be equal to
// STATUS_REPARSE because [(Status equals
// (STATUS_REPARSE_OBJECT or STATUS_REPARSE))
// && (Status != STATUS_REPARSE_OBJECT)]
//
ReparsedSymbolicLink = TRUE;
goto ParseFromRoot;
}
//
// We did not have a reparse object and the name
// does not start with a "\". Meaning we got back
// STATUS_REPASE, so now check if the directory
// is the root object directory and if so then
// we didn't the name otherwise we'll drop out of
// the inner loop and reparse true to get back to
// outer loop
//
} else if (RootDirectory == ObpRootDirectoryObject) {
Object = NULL;
Status = STATUS_OBJECT_NAME_NOT_FOUND;
Reparse = FALSE;
}
} else {
//
// We return object not found if we've exhausted
// the MaxReparse time
//
Object = NULL;
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
//
// We are not reparsing and if we did not get success then
// the object is null and we'll break out of our loops
//
} else if (!NT_SUCCESS( Status )) {
Object = NULL;
//
// We are not reparsing and we got back success but check
// if the object is null because that means we really didn't
// find the object, and then break out of our loops
//
// If the object is not null then we've been successful and
// prosperous so break out with the object set.
//
} else if (Object == NULL) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
break;
} else {
//
// At this point we do not have a parse routine or if there
// is a parse routine it is not for symbolic links or there
// may not be a specified insert object
//
// Check to see if we have exhausted the remaining name
//
if (RemainingName.Length == 0) {
//
// Check if the caller specified an object to insert.
// If specified then we'll break out of our loops with
// the object that we've found
//
if (!InsertObject) {
//
// The user did not specify an insert object
// so 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,
AccessCheckMode,
&Status )) {
Object = NULL;
break;
}
}
Status = ObReferenceObjectByPointer( Object,
0,
ObjectType,
AccessMode );
if (!NT_SUCCESS( Status )) {
Object = NULL;
}
}
break;
} else {
//
// There is some name remaining names to process
// if the directory we're looking at is the
// directory of object types and set ourselves
// up to parse it all over again.
//
if (ObjectHeader->Type == ObpDirectoryObjectType) {
ParentDirectory = Directory;
Directory = (POBJECT_DIRECTORY)Object;
} else {
//
// Otherwise there has been a mismatch so we'll
// set our error status and break out of the
// loops
//
Status = STATUS_OBJECT_TYPE_MISMATCH;
Object = NULL;
break;
}
}
}
}
}
//
// We can release the context if our search was unsuccesful.
// We still need the directory to be locked if a new object is
// inserted into that directory, until we finish the initialization
// (i.e SD, handle, ...). Note the object is visible now and could be accessed
// via name. We need to hold the directory lock until we are done.
//
if ( !NT_SUCCESS(Status) ) {
ObpReleaseLookupContext(LookupContext);
}
//
// If the device map has been referenced then dereference it
//
if (DeviceMap != NULL) {
ObfDereferenceDeviceMap(DeviceMap);
}
//
// At this point we've parsed the object name as much as possible
// going through symbolic links as necessary. So now set the
// output object pointer, and if we really did not find an object
// then we might need to modify the error status. If the
// status was repase or some success status then translate it
// to name not found.
//
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 the caller gave us a root directory to search (and we didn't
// zero out this value) then free up our reference
//
if (ARGUMENT_PRESENT( RootDirectoryHandle )) {
ObDereferenceObject( RootDirectory );
RootDirectoryHandle = NULL;
}
//
// And return to our caller
//
return( Status );
}
NTSTATUS
NtMakePermanentObject (
IN HANDLE Handle
)
/*++
Routine Description:
This routine makes the specified object permanent.
By default, only Local_System may make this call
Arguments:
Handle - Supplies a handle to the object being modified
Return Value:
An appropriate status value.
--*/
{
KPROCESSOR_MODE PreviousMode;
NTSTATUS Status;
PVOID Object;
OBJECT_HANDLE_INFORMATION HandleInformation;
POBJECT_HEADER ObjectHeader;
PAGED_CODE();
//
// Get previous processor mode and probe output argument if necessary.
//
PreviousMode = KeGetPreviousMode();
//
// The object is being changed to permanent, check if
// the caller has the appropriate privilege.
//
if (!SeSinglePrivilegeCheck( SeCreatePermanentPrivilege,
PreviousMode)) {
Status = STATUS_PRIVILEGE_NOT_HELD;
return( Status );
}
Status = ObReferenceObjectByHandle( Handle,
0,
(POBJECT_TYPE)NULL,
PreviousMode,
&Object,
&HandleInformation );
if (!NT_SUCCESS( Status )) {
return( Status );
}
//
// Make the object permanant. Note that the object should still
// have a name and directory entry because its handle count is not
// zero
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
//
// Other bits are set in this flags field by the handle database code. Synchronize with that.
//
ObpLockObject( ObjectHeader );
ObjectHeader->Flags |= OB_FLAG_PERMANENT_OBJECT;
//
// This routine releases the type mutex
//
ObpUnlockObject( ObjectHeader );
ObDereferenceObject( Object );
return( Status );
}