/*++ 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; }