/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
- * FILE: ntoskrnl/io/device.c
+ * FILE: ntoskrnl/io/iomgr/device.c
* PURPOSE: Device Object Management, including Notifications and Queues.
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Filip Navara (navaraf@reactos.org)
- * Hervé Poussineau (hpoussin@reactos.org)
+ * Hervé Poussineau (hpoussin@reactos.org)
+ * Pierre Schweitzer
*/
/* INCLUDES *******************************************************************/
extern LIST_ENTRY IopTapeFileSystemQueueHead;
extern ERESOURCE IopDatabaseResource;
+#define DACL_SET 4
+
/* PRIVATE FUNCTIONS **********************************************************/
VOID
IN IOP_DEVICE_LIST_OPERATION Type)
{
PDEVICE_OBJECT Previous;
+ KIRQL OldIrql;
+
+ /* Lock the Device list while we edit it */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
/* Check the type of operation */
if (Type == IopRemove)
while (Previous->NextDevice != DeviceObject)
{
/* Not this one, keep moving */
+ if (!Previous->NextDevice)
+ {
+ DPRINT1("Failed to remove PDO %p (not found)\n",
+ DeviceObject);
+
+ ASSERT(FALSE);
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+ return;
+ }
Previous = Previous->NextDevice;
}
DeviceObject->NextDevice = DriverObject->DeviceObject;
DriverObject->DeviceObject = DeviceObject;
}
+
+ /* Release the device list lock */
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
}
VOID
/* Check if we have a Security Descriptor */
if (DeviceObject->SecurityDescriptor)
{
- /* Free it */
- ExFreePoolWithTag(DeviceObject->SecurityDescriptor, TAG_SD);
+ /* Dereference it */
+ ObDereferenceSecurityDescriptor(DeviceObject->SecurityDescriptor, 1);
}
/* Remove the device from the list */
return Status;
}
+BOOLEAN
+NTAPI
+IopVerifyDeviceObjectOnStack(IN PDEVICE_OBJECT BaseDeviceObject,
+ IN PDEVICE_OBJECT TopDeviceObjectHint)
+{
+ KIRQL OldIrql;
+ BOOLEAN Result;
+ PDEVICE_OBJECT LoopObject;
+
+ ASSERT(BaseDeviceObject != NULL);
+
+ Result = FALSE;
+ /* Simply loop on the device stack and try to find our hint */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
+ for (LoopObject = BaseDeviceObject; ; LoopObject = LoopObject->AttachedDevice)
+ {
+ /* It was found, it's a success */
+ if (LoopObject == TopDeviceObjectHint)
+ {
+ Result = TRUE;
+ break;
+ }
+
+ /* End of the stack, that's a failure - default */
+ if (LoopObject == NULL)
+ {
+ break;
+ }
+ }
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+
+ return Result;
+}
+
+NTSTATUS
+NTAPI
+IopCreateSecurityDescriptorPerType(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ULONG Type,
+ OUT PULONG OutputFlags)
+{
+ PACL Dacl;
+ NTSTATUS Status;
+
+ /* Select the DACL the caller wants */
+ switch (Type)
+ {
+ case RestrictedPublic:
+ Dacl = SePublicDefaultDacl;
+ break;
+
+ case UnrestrictedPublic:
+ Dacl = SePublicDefaultUnrestrictedDacl;
+ break;
+
+ case RestrictedPublicOpen:
+ Dacl = SePublicOpenDacl;
+ break;
+
+ case UnrestrictedPublicOpen:
+ Dacl = SePublicOpenUnrestrictedDacl;
+ break;
+
+ case SystemDefault:
+ Dacl = SeSystemDefaultDacl;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ /* Create the SD and set the DACL caller wanted */
+ Status = RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
+ ASSERT(NT_SUCCESS(Status));
+ Status = RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
+
+ /* We've set DACL */
+ if (OutputFlags) *OutputFlags |= DACL_SET;
+
+ /* Done */
+ return Status;
+}
+
+PSECURITY_DESCRIPTOR
+NTAPI
+IopCreateDefaultDeviceSecurityDescriptor(IN DEVICE_TYPE DeviceType,
+ IN ULONG DeviceCharacteristics,
+ IN BOOLEAN HasDeviceName,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PACL * OutputDacl,
+ OUT PULONG OutputFlags)
+{
+ PACL Dacl;
+ ULONG AceId;
+ NTSTATUS Status;
+ PACCESS_ALLOWED_ACE Ace;
+ BOOLEAN AdminsSet, WorldSet;
+
+ PAGED_CODE();
+
+ /* Zero our output vars */
+ if (OutputFlags) *OutputFlags = 0;
+
+ *OutputDacl = NULL;
+
+ /* For FSD, easy use SePublicDefaultUnrestrictedDacl */
+ if (DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM ||
+ DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM ||
+ DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM ||
+ DeviceType == FILE_DEVICE_FILE_SYSTEM)
+ {
+ Status = IopCreateSecurityDescriptorPerType(SecurityDescriptor,
+ UnrestrictedPublic,
+ OutputFlags);
+ goto Quit;
+ }
+ /* For storage devices with a name and floppy attribute,
+ * use SePublicOpenUnrestrictedDacl
+ */
+ else if ((DeviceType != FILE_DEVICE_VIRTUAL_DISK &&
+ DeviceType != FILE_DEVICE_MASS_STORAGE &&
+ DeviceType != FILE_DEVICE_CD_ROM &&
+ DeviceType != FILE_DEVICE_DISK &&
+ DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM &&
+ DeviceType != FILE_DEVICE_NETWORK &&
+ DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
+ (HasDeviceName && BooleanFlagOn(DeviceCharacteristics, FILE_FLOPPY_DISKETTE)))
+ {
+ Status = IopCreateSecurityDescriptorPerType(SecurityDescriptor,
+ UnrestrictedPublicOpen,
+ OutputFlags);
+ goto Quit;
+ }
+
+ /* The rest...
+ * We will rely on SePublicDefaultUnrestrictedDacl as well
+ */
+ Dacl = ExAllocatePoolWithTag(PagedPool, SePublicDefaultUnrestrictedDacl->AclSize, 'eSoI');
+ if (Dacl == NULL)
+ {
+ return NULL;
+ }
+
+ /* Copy our DACL */
+ RtlCopyMemory(Dacl, SePublicDefaultUnrestrictedDacl, SePublicDefaultUnrestrictedDacl->AclSize);
+
+ /* Now, browse the DACL to make sure we have everything we want in them,
+ * including permissions
+ */
+ AceId = 0;
+ AdminsSet = FALSE;
+ WorldSet = FALSE;
+ while (NT_SUCCESS(RtlGetAce(Dacl, AceId, (PVOID *)&Ace)))
+ {
+ /* Admins must acess and in RWX, set it */
+ if (RtlEqualSid(SeAliasAdminsSid, &Ace->SidStart))
+ {
+ SetFlag(Ace->Mask, (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE));
+ AdminsSet = TRUE;
+ }
+
+ /* World can read a CD_ROM device */
+ if (DeviceType == FILE_DEVICE_CD_ROM && RtlEqualSid(SeWorldSid, &Ace->SidStart))
+ {
+ SetFlag(Ace->Mask, GENERIC_READ);
+ WorldSet = TRUE;
+ }
+
+ ++AceId;
+ }
+
+ /* AdminSid was present and set (otherwise, we have big trouble) */
+ ASSERT(AdminsSet);
+
+ /* If CD_ROM device, we've set world permissions */
+ if (DeviceType == FILE_DEVICE_CD_ROM) ASSERT(WorldSet);
+
+ /* Now our DACL is correct, setup the security descriptor */
+ RtlCreateSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
+ RtlSetDaclSecurityDescriptor(SecurityDescriptor, TRUE, Dacl, FALSE);
+
+ /* We've set DACL */
+ if (OutputFlags) *OutputFlags |= DACL_SET;
+
+ /* Return DACL to allow later freeing */
+ *OutputDacl = Dacl;
+ Status = STATUS_SUCCESS;
+
+Quit:
+ /* Only return SD if we succeed */
+ if (!NT_SUCCESS(Status))
+ {
+ return NULL;
+ }
+
+ return SecurityDescriptor;
+}
+
/* PUBLIC FUNCTIONS ***********************************************************/
/*
ULONG AlignedDeviceExtensionSize;
ULONG TotalSize;
HANDLE TempHandle;
+ PACL Dacl;
+ SECURITY_DESCRIPTOR SecurityDescriptor, *ReturnedSD;
PAGED_CODE();
/* Check if we have to generate a name */
/* Initialize the name */
RtlInitUnicodeString(&AutoName, AutoNameBuffer);
DeviceName = &AutoName;
- }
+ }
+
+ /* Get the security descriptor */
+ ReturnedSD = IopCreateDefaultDeviceSecurityDescriptor(DeviceType,
+ DeviceCharacteristics,
+ DeviceName != NULL,
+ &SecurityDescriptor,
+ &Dacl,
+ NULL);
/* Initialize the Object Attributes */
InitializeObjectAttributes(&ObjectAttributes,
DeviceName,
OBJ_KERNEL_HANDLE,
NULL,
- NULL);
+ ReturnedSD);
/* Honor exclusive flag */
if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
0,
0,
(PVOID*)&CreatedDeviceObject);
- if (!NT_SUCCESS(Status)) return Status;
+ if (!NT_SUCCESS(Status))
+ {
+ if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
+ return Status;
+ }
/* Clear the whole Object and extension so we don't null stuff manually */
RtlZeroMemory(CreatedDeviceObject, TotalSize);
Status = IopCreateVpb(CreatedDeviceObject);
if (!NT_SUCCESS(Status))
{
+ if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
/* Dereference the device object and fail */
ObDereferenceObject(CreatedDeviceObject);
return Status;
1,
(PVOID*)&CreatedDeviceObject,
&TempHandle);
- if (!NT_SUCCESS(Status)) return Status;
+ if (!NT_SUCCESS(Status))
+ {
+ if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
+ return Status;
+ }
/* Now do the final linking */
ObReferenceObject(DriverObject);
/* Close the temporary handle and return to caller */
ObCloseHandle(TempHandle, KernelMode);
*DeviceObject = CreatedDeviceObject;
+
+ if (Dacl != NULL) ExFreePoolWithTag(Dacl, 'eSoI');
+
return STATUS_SUCCESS;
}
/* Set the pending delete flag */
IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
+ /* Unlink with the power manager */
+ if (DeviceObject->Vpb) PoRemoveVolumeDevice(DeviceObject);
+
/* Check if the device object can be unloaded */
if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
}
{
ULONG ActualDevices = 1;
PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
+ KIRQL OldIrql;
+
+ /* Lock the Device list while we enumerate it */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
/* Find out how many devices we'll enumerate */
while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
*ActualNumberDeviceObjects = ActualDevices;
/* Check if we can support so many */
- if ((ActualDevices * 4) > DeviceObjectListSize)
+ if ((ActualDevices * sizeof(PDEVICE_OBJECT)) > DeviceObjectListSize)
{
/* Fail because the buffer was too small */
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
return STATUS_BUFFER_TOO_SMALL;
}
- /* Check if the caller only wanted the size */
+ /* Check if the caller wanted the device list */
if (DeviceObjectList)
{
/* Loop through all the devices */
}
}
+ /* Release the device list lock */
+ KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
+
/* Return the status */
return STATUS_SUCCESS;
}
/* Check if the extension is really present */
if (FileObject->FileObjectExtension)
{
- /* FIXME: Unhandled yet */
- DPRINT1("FOEs not supported\n");
- ASSERT(FALSE);
+ PFILE_OBJECT_EXTENSION FileObjectExtension;
+
+ /* Cast the buffer to something we understand */
+ FileObjectExtension = FileObject->FileObjectExtension;
+
+ /* Check if have a valid replacement top level device */
+ if (FileObjectExtension->TopDeviceObjectHint &&
+ IopVerifyDeviceObjectOnStack(DeviceObject,
+ FileObjectExtension->TopDeviceObjectHint))
+ {
+ /* Use this instead of returning the top level device */
+ return FileObjectExtension->TopDeviceObjectHint;
+ }
}
}