2020-09-30 17:17:25 +02:00

735 lines
19 KiB
C

/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
obdir.c
Abstract:
This module implements routines to manage directory objects.
--*/
#include "obp.h"
//
// Object type information for directories.
//
DECLSPEC_RDATA OBJECT_TYPE ObDirectoryObjectType = {
ExAllocatePoolWithTag,
ExFreePool,
NULL,
NULL,
NULL,
&ObpDefaultObject,
'eriD'
};
//
// Array lookup of the DOS drive letters for the DOS devices directory object.
//
PVOID ObpDosDevicesDriveLetterMap['Z' - 'A' + 1];
NTSTATUS
NtCreateDirectoryObject(
OUT PHANDLE DirectoryHandle,
IN POBJECT_ATTRIBUTES ObjectAttributes
)
/*++
Routine Description:
This routine creates a directory object with the supplied attributes.
Arguments:
DirectoryHandle - Supplies the location to receive the created handle.
ObjectAttributes - Supplies the name and parent directory of the new object.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
POBJECT_DIRECTORY Directory;
//
// Create the directory object.
//
status = ObCreateObject(&ObDirectoryObjectType, ObjectAttributes,
sizeof(OBJECT_DIRECTORY), (PVOID*)&Directory);
if (NT_SUCCESS(status)) {
RtlZeroMemory(Directory, sizeof(OBJECT_DIRECTORY));
status = ObInsertObject(Directory, ObjectAttributes, 0, DirectoryHandle);
}
return status;
}
NTSTATUS
NtOpenDirectoryObject(
OUT PHANDLE DirectoryHandle,
IN POBJECT_ATTRIBUTES ObjectAttributes
)
/*++
Routine Description:
This routine opens an existing directory object with the supplied
attributes.
Arguments:
DirectoryHandle - Supplies the location to receive the opened handle.
ObjectAttributes - Supplies the name and parent directory of the desired
object.
Return Value:
Status of operation.
--*/
{
return ObOpenObjectByName(ObjectAttributes, &ObDirectoryObjectType, NULL,
DirectoryHandle);
}
NTSTATUS
NtQueryDirectoryObject(
IN HANDLE DirectoryHandle,
OUT PVOID Buffer,
IN ULONG Length,
IN BOOLEAN RestartScan,
IN OUT PULONG Context,
OUT PULONG ReturnedLength OPTIONAL
)
/*++
Routine Description:
This routine returns the next directory entry from the supplied object
directory.
Arguments:
DirectoryHandle - Supplies the handle of the object directory.
Buffer - Supplies the buffer to receive the information.
Length - Supplies the length of the above buffer.
RestartScan - Supplies whether or not the scan should restart.
Context - Supplies data that controls the current position of the
enumeration.
ReturnedLength - Supplies the location to receive the number of bytes copied
to the buffer or the number of bytes required if the buffer is too
small.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
POBJECT_DIRECTORY_INFORMATION DirectoryInformation;
POBJECT_DIRECTORY Directory;
ULONG BytesRequired;
ULONG DesiredIndex;
ULONG CurrentIndex;
KIRQL OldIrql;
ULONG HashBucket;
POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo;
ULONG NameBytesToCopy;
POSTR ReturnedObjectName;
//
// Verify that we have enough space to write out at least the header.
//
DirectoryInformation = (POBJECT_DIRECTORY_INFORMATION)Buffer;
if (Length < sizeof(OBJECT_DIRECTORY_INFORMATION)) {
return STATUS_INVALID_PARAMETER;
}
//
// Reference the directory object.
//
status = ObReferenceObjectByHandle(DirectoryHandle, &ObDirectoryObjectType,
(PVOID*)&Directory);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Assume that we won't find any more entries.
//
status = STATUS_NO_MORE_ENTRIES;
BytesRequired = 0;
//
// Figure out which entry we want to find.
//
DesiredIndex = RestartScan ? 0 : *Context;
CurrentIndex = 0;
//
// Loop over all of the hash buckets until we find our desired index.
//
ObpAcquireObjectManagerLock(&OldIrql);
for (HashBucket = 0; HashBucket < OB_NUMBER_HASH_BUCKETS; HashBucket++) {
ObjectHeaderNameInfo = Directory->HashBuckets[HashBucket];
while (ObjectHeaderNameInfo != NULL) {
if (CurrentIndex == DesiredIndex) {
//
// Compute the number of bytes required to copy the string and
// the number of bytes that we can actually copy.
//
NameBytesToCopy = ObjectHeaderNameInfo->Name.Length;
BytesRequired = sizeof(OBJECT_DIRECTORY_INFORMATION) +
NameBytesToCopy;
if (BytesRequired <= Length) {
status = STATUS_SUCCESS;
} else {
NameBytesToCopy = Length - sizeof(OBJECT_DIRECTORY_INFORMATION);
status = STATUS_BUFFER_TOO_SMALL;
}
ReturnedObjectName = (POCHAR)(DirectoryInformation + 1);
DirectoryInformation->Type =
OBJECT_HEADER_NAME_INFO_TO_OBJECT_HEADER(ObjectHeaderNameInfo)->Type->PoolTag;
DirectoryInformation->Name.Buffer = ReturnedObjectName;
DirectoryInformation->Name.Length =
ObjectHeaderNameInfo->Name.Length;
DirectoryInformation->Name.MaximumLength =
ObjectHeaderNameInfo->Name.Length;
RtlCopyMemory(ReturnedObjectName,
ObjectHeaderNameInfo->Name.Buffer, NameBytesToCopy);
//
// Bump up the index number for the next iteration.
//
*Context = DesiredIndex + 1;
goto FoundDesiredIndex;
}
ObjectHeaderNameInfo = ObjectHeaderNameInfo->ChainLink;
CurrentIndex++;
}
}
FoundDesiredIndex:
ObpReleaseObjectManagerLock(OldIrql);
//
// Dereference the object and return the number of bytes that we copied or
// that are required.
//
ObDereferenceObject(Directory);
if (ReturnedLength != NULL) {
*ReturnedLength = BytesRequired;
}
return status;
}
ULONG
FASTCALL
ObpComputeHashIndex(
IN POBJECT_STRING ElementName
)
/*++
Routine Description:
This routine computes the hash index for the supplied name.
Arguments:
ElementName - Supplies the name to compute the hash value for.
Return Value:
The hash index.
--*/
{
ULONG HashIndex;
PUCHAR Buffer;
PUCHAR BufferEnd;
UCHAR Char;
HashIndex = 0;
Buffer = (PUCHAR)ElementName->Buffer;
BufferEnd = Buffer + ElementName->Length;
while (Buffer < BufferEnd) {
Char = *Buffer++;
//
// Don't allow extended characters to change the hash value since we're
// going to be doing case insensitive comparisions.
//
if (Char >= 0x80) {
continue;
}
//
// Force any upper case characters to be lower case.
//
Char |= 0x20;
HashIndex += (HashIndex << 1) + (HashIndex >> 1) + Char;
}
return HashIndex % OB_NUMBER_HASH_BUCKETS;
}
BOOLEAN
ObpLookupElementNameInDirectory(
IN POBJECT_DIRECTORY Directory,
IN POBJECT_STRING ElementName,
IN BOOLEAN ResolveSymbolicLink,
OUT PVOID *ReturnedObject
)
/*++
Routine Description:
This routine searches the object directory for the supplied object name.
Arguments:
Directory - Supplies the object directory to search.
ElementName - Supplies the name of the object to find.
ResolveSymbolicLink - Supplies TRUE if the returned object should be the
target of a symbolic link
ReturnedObject - Supplies the location to receive the object if found.
Return Value:
Returns TRUE if the element name was found, else FALSE.
--*/
{
OCHAR DriveLetter;
PVOID Object;
ULONG HashIndex;
POBJECT_HEADER_NAME_INFO ObjectHeaderNameInfo;
ObpAssertObjectManagerLock();
//
// If we're looking up a drive letter relative to the DOS devices directory
// and we're allowed to resolve symbolic links, then do a quick array lookup
// instead of walking the hash table.
//
if ((Directory == ObpDosDevicesDirectoryObject) &&
ResolveSymbolicLink &&
(ElementName->Length == sizeof(OCHAR) * 2) &&
(ElementName->Buffer[1] == (OCHAR)':')) {
DriveLetter = ElementName->Buffer[0];
if (DriveLetter >= 'a' && DriveLetter <= 'z') {
Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'a'];
} else if (DriveLetter >= 'A' && DriveLetter <= 'Z') {
Object = ObpDosDevicesDriveLetterMap[DriveLetter - 'A'];
} else {
Object = NULL;
}
if (Object != NULL) {
*ReturnedObject = Object;
return TRUE;
}
}
//
// Compute the hash index for the element name.
//
HashIndex = ObpComputeHashIndex(ElementName);
//
// Walk through the object's on this hash chain.
//
ObjectHeaderNameInfo = Directory->HashBuckets[HashIndex];
while (ObjectHeaderNameInfo != NULL) {
if (RtlEqualObjectString(&ObjectHeaderNameInfo->Name, ElementName, TRUE)) {
Object = OBJECT_HEADER_NAME_INFO_TO_OBJECT(ObjectHeaderNameInfo);
//
// Resolve the symbolic link if requested to.
//
if (ResolveSymbolicLink &&
(OBJECT_TO_OBJECT_HEADER(Object)->Type == &ObSymbolicLinkObjectType)) {
Object = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTargetObject;
}
*ReturnedObject = Object;
return TRUE;
}
ObjectHeaderNameInfo = ObjectHeaderNameInfo->ChainLink;
}
*ReturnedObject = NULL;
return FALSE;
}
NTSTATUS
ObpReferenceObjectByName(
IN HANDLE RootDirectoryHandle,
IN POBJECT_STRING ObjectName,
IN ULONG Attributes,
IN POBJECT_TYPE ObjectType,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *ReturnedObject
)
/*++
Routine Description:
This routine references the object with the supplied name and attributes.
Arguments:
RootDirectoryHandle - Supplies the handle to the root directory to begin
searching from.
ObjectName - Supplies the name of the object to reference.
Attributes - Supplies the attributes of the desired object.
ObjectType - Optionally supplies the desired type for the returned object.
ParseContext - Supplies state used while parsing the object's attributes.
ReturnedObject - Supplies the location to receive the object.
Return Value:
Status of operation.
--*/
{
NTSTATUS status;
KIRQL OldIrql;
OBJECT_STRING RemainingName;
OBJECT_STRING ElementName;
BOOLEAN ResolveSymbolicLink;
PVOID FoundObject;
POBJECT_HEADER ObjectHeader;
POBJECT_DIRECTORY Directory;
PVOID ParsedObject;
*ReturnedObject = NULL;
ObpAcquireObjectManagerLock(&OldIrql);
if (ObjectName != NULL) {
RemainingName = *ObjectName;
} else {
RtlZeroMemory(&RemainingName, sizeof(OBJECT_STRING));
}
ResolveSymbolicLink = TRUE;
//
// Determine whether we parse the object name from an absolute or
// relative root directory.
//
if (RootDirectoryHandle != NULL) {
if (RootDirectoryHandle == ObDosDevicesDirectory()) {
//
// Reference the root of the DOS devices space.
//
FoundObject = ObpDosDevicesDirectoryObject;
} else if (RootDirectoryHandle == ObWin32NamedObjectsDirectory()) {
//
// Reference the root of the Win32 named objects space.
//
FoundObject = ObpWin32NamedObjectsDirectoryObject;
} else {
//
// Reference the root directory handle.
//
FoundObject = (POBJECT_DIRECTORY)ObpGetObjectHandleContents(RootDirectoryHandle);
if (FoundObject == NULL) {
status = STATUS_INVALID_HANDLE;
goto CleanupAndExit;
}
}
//
// Verify that this is not an absolute path.
//
if ((RemainingName.Length != 0) &&
(RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) {
status = STATUS_OBJECT_NAME_INVALID;
goto CleanupAndExit;
}
goto OpenRootDirectory;
}
//
// Verify that this is an absolute path.
//
if ((RemainingName.Length == 0) ||
(RemainingName.Buffer[0] != OBJ_NAME_PATH_SEPARATOR)) {
status = STATUS_OBJECT_NAME_INVALID;
goto CleanupAndExit;
}
//
// Reference the global root directory handle.
//
FoundObject = ObpRootDirectoryObject;
//
// Check if we're supposed to open the root directory.
//
if (RemainingName.Length == sizeof(OCHAR)) {
//
// Advance past the backslash.
//
RemainingName.Buffer++;
RemainingName.Length = 0;
RemainingName.MaximumLength = 0;
goto OpenRootDirectory;
}
//
// Process the object name.
//
for (;;) {
Directory = (POBJECT_DIRECTORY)FoundObject;
ASSERT(OBJECT_TO_OBJECT_HEADER(Directory)->Type == &ObDirectoryObjectType);
ObDissectName(RemainingName, &ElementName, &RemainingName);
if (RemainingName.Length != 0) {
//
// Verify that there aren't multiple backslashes in the name.
//
if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
status = STATUS_OBJECT_NAME_INVALID;
goto CleanupAndExit;
}
} else {
//
// If the caller is trying to access a symbolic link object, then
// don't resolve symbolic links for the last element.
//
if (ObjectType == &ObSymbolicLinkObjectType) {
ResolveSymbolicLink = FALSE;
}
}
//
// Search for the object in the directory and return an error if it
// doesn't already exist.
//
if (!ObpLookupElementNameInDirectory(Directory, &ElementName,
ResolveSymbolicLink, &FoundObject)) {
status = (RemainingName.Length != 0) ?
STATUS_OBJECT_PATH_NOT_FOUND : STATUS_OBJECT_NAME_NOT_FOUND;
goto CleanupAndExit;
}
OpenRootDirectory:
ObjectHeader = OBJECT_TO_OBJECT_HEADER(FoundObject);
//
// If we've consumed the entire path, then we found the object, so
// return success.
//
if (RemainingName.Length == 0) {
//
// If the object has a parse procedure, then we need to invoke it
// since it may want to return a different object (such as when we
// open a device object, we really want to return a file object).
//
if (ObjectHeader->Type->ParseProcedure != NULL) {
goto InvokeParseProcedure;
}
//
// Verify that the found object matches the requested object type.
//
if ((ObjectType != NULL) && (ObjectType != ObjectHeader->Type)) {
status = STATUS_OBJECT_TYPE_MISMATCH;
goto CleanupAndExit;
}
ObjectHeader->PointerCount++;
*ReturnedObject = FoundObject;
status = STATUS_SUCCESS;
goto CleanupAndExit;
}
//
// Only continue parsing in this loop if we found a directory object.
//
if (ObjectHeader->Type != &ObDirectoryObjectType) {
//
// If the object has a parse procedure, use it to evaluate the
// remainder of the string. If not, then we can't go any further.
//
if (ObjectHeader->Type->ParseProcedure == NULL) {
status = STATUS_OBJECT_PATH_NOT_FOUND;
goto CleanupAndExit;
}
//
// Make sure the object stays alive after we drop the object manager
// lock.
//
InvokeParseProcedure:
ObjectHeader->PointerCount++;
ObpReleaseObjectManagerLock(OldIrql);
//
// If this isn't a file object that we're parsing, then back the
// remaining name up to the path separator. If we do have a file
// object, then it must be from a symbolic link to a directory. For
// a directory, we want it to appear that we're doing a relative
// lookup, so we don't want the leading backslash.
//
if (ObjectHeader->Type != &IoFileObjectType &&
(RemainingName.Buffer > ObjectName->Buffer)) {
RemainingName.Buffer--;
RemainingName.Length += sizeof(OCHAR);
RemainingName.MaximumLength = RemainingName.Length;
ASSERT(RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR);
}
//
// Invoke the object's parse procedure.
//
ParsedObject = NULL;
status = ObjectHeader->Type->ParseProcedure(FoundObject, ObjectType,
Attributes, ObjectName, &RemainingName, ParseContext,
&ParsedObject);
ObDereferenceObject(FoundObject);
//
// Verify that the parsed object matches the requested object type.
//
if (NT_SUCCESS(status)) {
ASSERT(ParsedObject != NULL);
if ((ObjectType == NULL) ||
(ObjectType == OBJECT_TO_OBJECT_HEADER(ParsedObject)->Type)) {
*ReturnedObject = ParsedObject;
status = STATUS_SUCCESS;
} else {
ObDereferenceObject(ParsedObject);
status = STATUS_OBJECT_TYPE_MISMATCH;
}
}
return status;
}
}
CleanupAndExit:
ObpReleaseObjectManagerLock(OldIrql);
return status;
}