reactos/ntoskrnl/ob/devicemap.c

692 lines
19 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ob/devicemap.c
* PURPOSE: Device map implementation
* PROGRAMMERS: Eric Kohl (eric.kohl@reactos.org)
* Alex Ionescu (alex.ionescu@reactos.org)
* Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES ***************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
ULONG ObpLUIDDeviceMapsDisabled;
ULONG ObpLUIDDeviceMapsEnabled;
/* PRIVATE FUNCTIONS ******************************************************/
NTSTATUS
NTAPI
ObSetDeviceMap(IN PEPROCESS Process,
IN HANDLE DirectoryHandle)
{
POBJECT_DIRECTORY DirectoryObject = NULL;
PDEVICE_MAP DeviceMap = NULL, NewDeviceMap = NULL, OldDeviceMap;
NTSTATUS Status;
PEPROCESS WorkProcess;
BOOLEAN MakePermanant = FALSE;
Status = ObReferenceObjectByHandle(DirectoryHandle,
DIRECTORY_TRAVERSE,
ObpDirectoryObjectType,
KeGetPreviousMode(),
(PVOID*)&DirectoryObject,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
return Status;
}
/* Allocate and initialize a new device map */
DeviceMap = ExAllocatePoolWithTag(PagedPool,
sizeof(*DeviceMap),
'mDbO');
if (DeviceMap == NULL)
{
ObDereferenceObject(DirectoryObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the device map */
RtlZeroMemory(DeviceMap, sizeof(*DeviceMap));
DeviceMap->ReferenceCount = 1;
DeviceMap->DosDevicesDirectory = DirectoryObject;
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Attach the device map to the directory object */
if (DirectoryObject->DeviceMap == NULL)
{
DirectoryObject->DeviceMap = DeviceMap;
}
else
{
NewDeviceMap = DeviceMap;
/* There's already a device map,
so reuse it */
DeviceMap = DirectoryObject->DeviceMap;
++DeviceMap->ReferenceCount;
}
/* Caller gave a process, use it */
if (Process != NULL)
{
WorkProcess = Process;
}
/* If no process given, use current and
* set system device map */
else
{
WorkProcess = PsGetCurrentProcess();
ObSystemDeviceMap = DeviceMap;
}
/* If current object isn't system one, save system one in current
* device map */
if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
{
/* We also need to make the object permanant */
DeviceMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
MakePermanant = TRUE;
}
/* Save old process device map */
OldDeviceMap = WorkProcess->DeviceMap;
/* Attach the device map to the process */
WorkProcess->DeviceMap = DeviceMap;
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* If we have to make the object permamant, do it now */
if (MakePermanant)
{
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO HeaderNameInfo;
ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
ObpEnterObjectTypeMutex(ObjectHeader->Type);
if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
{
ObjectHeader->Flags |= OB_FLAG_PERMANENT;
}
ObpLeaveObjectTypeMutex(ObjectHeader->Type);
if (HeaderNameInfo != NULL)
{
ObpDereferenceNameInfo(HeaderNameInfo);
}
}
/* Release useless device map if required */
if (NewDeviceMap != NULL)
{
ObDereferenceObject(DirectoryObject);
ExFreePoolWithTag(NewDeviceMap, 'mDbO');
}
/* And dereference previous process device map */
if (OldDeviceMap != NULL)
{
ObfDereferenceDeviceMap(OldDeviceMap);
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
ObSetDirectoryDeviceMap(OUT PDEVICE_MAP * DeviceMap,
IN HANDLE DirectoryHandle)
{
POBJECT_DIRECTORY DirectoryObject = NULL;
PDEVICE_MAP LocalMap = NULL, NewDeviceMap = NULL;
NTSTATUS Status;
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO HeaderNameInfo;
Status = ObReferenceObjectByHandle(DirectoryHandle,
DIRECTORY_TRAVERSE,
ObpDirectoryObjectType,
KernelMode,
(PVOID*)&DirectoryObject,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT("ObReferenceObjectByHandle() failed (Status 0x%08lx)\n", Status);
return Status;
}
/* Allocate and initialize a new device map */
LocalMap = ExAllocatePoolWithTag(PagedPool,
sizeof(*LocalMap),
'mDbO');
if (LocalMap == NULL)
{
ObDereferenceObject(DirectoryObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Initialize the device map */
RtlZeroMemory(LocalMap, sizeof(*LocalMap));
LocalMap->ReferenceCount = 1;
LocalMap->DosDevicesDirectory = DirectoryObject;
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Attach the device map to the directory object */
if (DirectoryObject->DeviceMap == NULL)
{
DirectoryObject->DeviceMap = LocalMap;
}
else
{
NewDeviceMap = LocalMap;
/* There's already a device map,
so reuse it */
LocalMap = DirectoryObject->DeviceMap;
++LocalMap->ReferenceCount;
}
/* If current object isn't system one, save system one in current
* device map */
if (DirectoryObject != ObSystemDeviceMap->DosDevicesDirectory)
{
/* We also need to make the object permanant */
LocalMap->GlobalDosDevicesDirectory = ObSystemDeviceMap->DosDevicesDirectory;
}
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
if (DeviceMap != NULL)
{
*DeviceMap = LocalMap;
}
/* Caller expects us to make the object permanant, so do it! */
ObjectHeader = OBJECT_TO_OBJECT_HEADER(DirectoryObject);
HeaderNameInfo = ObpReferenceNameInfo(ObjectHeader);
ObpEnterObjectTypeMutex(ObjectHeader->Type);
if (HeaderNameInfo != NULL && HeaderNameInfo->Directory != NULL)
{
ObjectHeader->Flags |= OB_FLAG_PERMANENT;
}
ObpLeaveObjectTypeMutex(ObjectHeader->Type);
if (HeaderNameInfo != NULL)
{
ObpDereferenceNameInfo(HeaderNameInfo);
}
/* Release useless device map if required */
if (NewDeviceMap != NULL)
{
ObDereferenceObject(DirectoryObject);
ExFreePoolWithTag(NewDeviceMap, 'mDbO');
}
return Status;
}
NTSTATUS
NTAPI
ObpSetCurrentProcessDeviceMap(VOID)
{
PTOKEN Token;
LUID LogonId;
NTSTATUS Status;
PEPROCESS CurrentProcess;
LUID SystemLuid = SYSTEM_LUID;
PDEVICE_MAP DeviceMap, OldDeviceMap;
/* Get our impersonation token */
CurrentProcess = PsGetCurrentProcess();
Token = PsReferencePrimaryToken(CurrentProcess);
if (Token == NULL)
{
return STATUS_NO_TOKEN;
}
/* Query the Logon ID */
Status = SeQueryAuthenticationIdToken(Token, &LogonId);
if (!NT_SUCCESS(Status))
{
goto done;
}
/* If that's system, then use system device map */
if (RtlEqualLuid(&LogonId, &SystemLuid))
{
DeviceMap = ObSystemDeviceMap;
}
/* Otherwise ask Se for the device map */
else
{
Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
if (!NT_SUCCESS(Status))
{
/* Normalize failure status */
Status = STATUS_OBJECT_PATH_INVALID;
goto done;
}
}
/* Fail if no device map */
if (DeviceMap == NULL)
{
Status = STATUS_OBJECT_PATH_INVALID;
goto done;
}
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Save old device map attached to the process */
OldDeviceMap = CurrentProcess->DeviceMap;
/* Set new device map & reference it */
++DeviceMap->ReferenceCount;
CurrentProcess->DeviceMap = DeviceMap;
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* If we had a device map, dereference it */
if (OldDeviceMap != NULL)
{
ObfDereferenceDeviceMap(OldDeviceMap);
}
done:
/* We're done with the token! */
ObDereferenceObject(Token);
return Status;
}
PDEVICE_MAP
NTAPI
ObpReferenceDeviceMap(VOID)
{
LUID LogonId;
NTSTATUS Status;
PTOKEN Token = NULL;
PDEVICE_MAP DeviceMap;
PETHREAD CurrentThread;
BOOLEAN LookingForSystem;
LUID SystemLuid = SYSTEM_LUID;
BOOLEAN CopyOnOpen, EffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
LookingForSystem = FALSE;
/* If LUID mapping is enable, try to get appropriate device map */
if (ObpLUIDDeviceMapsEnabled != 0)
{
/* In case of impersonation, we've got a bit of work to do */
CurrentThread = PsGetCurrentThread();
if (CurrentThread->ActiveImpersonationInfo)
{
/* Get impersonation token */
Token = PsReferenceImpersonationToken(CurrentThread,
&CopyOnOpen,
&EffectiveOnly,
&ImpersonationLevel);
/* Get logon LUID */
if (Token != NULL)
{
Status = SeQueryAuthenticationIdToken(Token, &LogonId);
}
else
{
/* Force failure */
Status = STATUS_NO_TOKEN;
}
/* If we got logon LUID */
if (NT_SUCCESS(Status))
{
/*
* Check it's not system, system is easy to handle,
* we just need to return ObSystemDeviceMap
*/
if (!RtlEqualLuid(&LogonId, &SystemLuid))
{
/* Ask Se for the device map */
Status = SeGetLogonIdDeviceMap(&LogonId, &DeviceMap);
if (NT_SUCCESS(Status))
{
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Reference the device map if any */
if (DeviceMap != NULL)
{
++DeviceMap->ReferenceCount;
}
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* If we got the device map, we're done! */
if (DeviceMap != NULL)
{
ObDereferenceObject(Token);
return DeviceMap;
}
}
}
else
{
LookingForSystem = TRUE;
}
}
}
/*
* Fall back case of the LUID mapping, make sure there's a
* a device map attached to the current process
*/
if (PsGetCurrentProcess()->DeviceMap == NULL &&
!NT_SUCCESS(ObpSetCurrentProcessDeviceMap()))
{
/* We may have failed after we got impersonation token */
if (Token != NULL)
{
ObDereferenceObject(Token);
}
return NULL;
}
}
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* If we're looking for system map, use it */
if (LookingForSystem)
{
DeviceMap = ObSystemDeviceMap;
}
/* Otherwise, use current process device map */
else
{
DeviceMap = PsGetCurrentProcess()->DeviceMap;
}
/* If we got one, reference it */
if (DeviceMap != NULL)
{
++DeviceMap->ReferenceCount;
}
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* We may have impersonation token (if we failed in impersonation branch) */
if (Token != NULL)
{
ObDereferenceObject(Token);
}
/* Return the potentially found device map */
return DeviceMap;
}
VOID
NTAPI
ObDereferenceDeviceMap(IN PEPROCESS Process)
{
PDEVICE_MAP DeviceMap;
DPRINT("ObDereferenceDeviceMap()\n");
/* Get the pointer to this process devicemap and reset it
holding the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
DeviceMap = Process->DeviceMap;
Process->DeviceMap = NULL;
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* Continue only if there is a device map */
if (DeviceMap != NULL)
ObfDereferenceDeviceMap(DeviceMap);
}
VOID
FASTCALL
ObfDereferenceDeviceMap(IN PDEVICE_MAP DeviceMap)
{
DPRINT("ObfDereferenceDeviceMap()\n");
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Decrement the reference counter */
DeviceMap->ReferenceCount--;
DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
/* Leave, if there are still references to this device map */
if (DeviceMap->ReferenceCount != 0)
{
/* Release the device map lock and leave */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
return;
}
/* Nobody is referencing it anymore, unlink the DOS directory */
DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
/* Release the devicemap lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* Dereference the DOS Devices Directory and free the Device Map */
ObMakeTemporaryObject(DeviceMap->DosDevicesDirectory);
ObDereferenceObject(DeviceMap->DosDevicesDirectory);
ExFreePoolWithTag(DeviceMap, 'mDbO');
}
VOID
NTAPI
ObInheritDeviceMap(IN PEPROCESS Parent,
IN PEPROCESS Process)
{
PDEVICE_MAP DeviceMap;
DPRINT("ObInheritDeviceMap()\n");
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Get the parent process device map or the system device map */
DeviceMap = (Parent != NULL) ? Parent->DeviceMap : ObSystemDeviceMap;
if (DeviceMap != NULL)
{
/* Reference the device map and attach it to the new process */
DeviceMap->ReferenceCount++;
DPRINT("ReferenceCount: %lu\n", DeviceMap->ReferenceCount);
Process->DeviceMap = DeviceMap;
}
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
}
NTSTATUS
NTAPI
ObQueryDeviceMapInformation(
_In_opt_ PEPROCESS Process,
_Out_ PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo,
_In_ ULONG Flags)
{
PDEVICE_MAP DeviceMap = NULL, GlobalDeviceMap;
BOOLEAN Dereference;
PROCESS_DEVICEMAP_INFORMATION MapInfo;
ULONG BitMask, i;
BOOLEAN ReturnAny;
NTSTATUS Status;
/* Validate flags */
if (Flags & ~PROCESS_LUID_DOSDEVICES_ONLY)
{
return STATUS_INVALID_PARAMETER;
}
Dereference = FALSE;
/* Do we want to return anything? */
ReturnAny = ~Flags & PROCESS_LUID_DOSDEVICES_ONLY;
/* If LUID mappings are enabled... */
if (ObpLUIDDeviceMapsEnabled != 0)
{
/* Check for process parameter validness */
if (Process != NULL && Process != PsGetCurrentProcess())
{
return STATUS_INVALID_PARAMETER;
}
/* And get the device map */
DeviceMap = ObpReferenceDeviceMap();
}
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/*
* If we had a device map, if because of LUID mappings,
* we'll have to dereference it afterwards
*/
if (DeviceMap != NULL)
{
Dereference = TRUE;
}
else
{
/* Get the process device map or the system device map */
DeviceMap = (Process != NULL) ? Process->DeviceMap : ObSystemDeviceMap;
}
/* Fail if no device map */
if (DeviceMap == NULL)
{
KeReleaseGuardedMutex(&ObpDeviceMapLock);
return STATUS_END_OF_FILE;
}
/* At that point, assume success */
Status = STATUS_SUCCESS;
/* Try to get the global device map if any */
GlobalDeviceMap = DeviceMap;
if (DeviceMap->GlobalDosDevicesDirectory != NULL)
{
if (DeviceMap->GlobalDosDevicesDirectory->DeviceMap != NULL)
{
GlobalDeviceMap = DeviceMap->GlobalDosDevicesDirectory->DeviceMap;
}
}
/* Now, setup our device map info, especially drive types */
MapInfo.Query.DriveMap = DeviceMap->DriveMap;
/* Browse every device */
for (i = 0, BitMask = 1; i < 32; ++i, BitMask *= 2)
{
/* Set the type given current device map */
MapInfo.Query.DriveType[i] = DeviceMap->DriveType[i];
/*
* If device is not existing and we're asked to return
* more than just LUID mapped, get the entry
* from global device map if not remote
*/
if (!(MapInfo.Query.DriveMap & BitMask) && ReturnAny)
{
if (ObpLUIDDeviceMapsEnabled != 0 ||
(GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_REMOTE &&
GlobalDeviceMap->DriveType[i] != DOSDEVICE_DRIVE_CALCULATE))
{
MapInfo.Query.DriveType[i] = GlobalDeviceMap->DriveType[i];
MapInfo.Query.DriveMap |= BitMask & GlobalDeviceMap->DriveMap;
}
}
}
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
/* Dereference LUID device map */
if (Dereference)
{
ObfDereferenceDeviceMap(DeviceMap);
}
/* Copy back data */
_SEH2_TRY
{
RtlCopyMemory(DeviceMapInfo, &MapInfo, sizeof(PROCESS_DEVICEMAP_INFORMATION));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
return Status;
}
ULONG
NTAPI
ObIsLUIDDeviceMapsEnabled(VOID)
{
return ObpLUIDDeviceMapsEnabled;
}
#if 0
NTSTATUS
NTAPI
ObIsDosDeviceLocallyMapped(
IN ULONG Index,
OUT PUCHAR DosDeviceState)
{
/* Check the index */
if (Index < 1 || Index > 26)
return STATUS_INVALID_PARAMETER;
/* Acquire the device map lock */
KeAcquireGuardedMutex(&ObpDeviceMapLock);
/* Get drive mapping status */
*DosDeviceState = (ObSystemDeviceMap->DriveMap & (1 << Index)) != 0;
/* Release the device map lock */
KeReleaseGuardedMutex(&ObpDeviceMapLock);
return STATUS_SUCCESS;
}
#endif
/* EOF */