windows-nt/Source/XPSP1/NT/base/ntos/io/iomgr/qsfs.c

966 lines
30 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
qsfs.c
Abstract:
This module contains the code to implement the NtQueryVolumeInformationFile
and NtSetVolumeInformationFile system services for the NT I/O system.
Author:
Darryl E. Havens (darrylh) 22-Jun-1989
Environment:
Kernel mode only
Revision History:
--*/
#include "iomgr.h"
#pragma hdrstop
#include <ioevent.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtQueryVolumeInformationFile)
#pragma alloc_text(PAGE, NtSetVolumeInformationFile)
#endif
NTSTATUS
NtQueryVolumeInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FsInformation,
IN ULONG Length,
IN FS_INFORMATION_CLASS FsInformationClass
)
/*++
Routine Description:
This service returns information about the volume associated with the
FileHandle parameter. The information returned in the buffer is defined
by the FsInformationClass parameter. The legal values for this parameter
are as follows:
o FileFsVolumeInformation
o FileFsSizeInformation
o FileFsDeviceInformation
o FileFsAttributeInformation
Arguments:
FileHandle - Supplies a handle to an open volume, directory, or file
for which information about the volume is returned.
IoStatusBlock - Address of the caller's I/O status block.
FsInformation - Supplies a buffer to receive the requested information
returned about the volume.
Length - Supplies the length, in bytes, of the FsInformation buffer.
FsInformationClass - Specifies the type of information which should be
returned about the volume.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
PIRP irp;
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
PKEVENT event = (PKEVENT) NULL;
KPROCESSOR_MODE requestorMode;
PIO_STACK_LOCATION irpSp;
IO_STATUS_BLOCK localIoStatus;
BOOLEAN synchronousIo;
PETHREAD CurrentThread;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
CurrentThread = PsGetCurrentThread ();
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
if (requestorMode != KernelMode) {
//
// Ensure that the FsInformationClass parameter is legal for querying
// information about the volume.
//
if ((ULONG) FsInformationClass >= FileFsMaximumInformation ||
IopQueryFsOperationLength[FsInformationClass] == 0) {
return STATUS_INVALID_INFO_CLASS;
}
//
// Finally, ensure that the supplied buffer is large enough to contain
// the information associated with the specified query operation that
// is to be performed.
//
if (Length < (ULONG) IopQueryFsOperationLength[FsInformationClass]) {
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// The caller's access mode is not kernel so probe each of the arguments
// and capture them as necessary. If any failures occur, the condition
// handler will be invoked to handle them. It will simply cleanup and
// return an access violation status code back to the system service
// dispatcher.
//
try {
//
// The IoStatusBlock parameter must be writeable by the caller.
//
ProbeForWriteIoStatus( IoStatusBlock );
//
// The FsInformation buffer must be writeable by the caller.
//
#if defined(_X86_)
ProbeForWrite( FsInformation, Length, sizeof( ULONG ) );
#elif defined(_WIN64)
//
// If we are a wow64 process, follow the X86 rules
//
if (PsGetCurrentProcessByThread(CurrentThread)->Wow64Process) {
ProbeForWrite( FsInformation, Length, sizeof( ULONG ) );
} else {
ProbeForWrite( FsInformation,
Length,
IopQuerySetFsAlignmentRequirement[FsInformationClass] );
}
#else
ProbeForWrite( FsInformation,
Length,
IopQuerySetFsAlignmentRequirement[FsInformationClass] );
#endif
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred probing the caller's parameters.
// Simply return an appropriate error status code.
//
return GetExceptionCode();
}
}
//
// There were no blatant errors so far, so reference the file object so
// the target device object can be found. Note that if the handle does
// not refer to a file object, or if the caller does not have the required
// access to the file, then it will fail.
//
status = ObReferenceObjectByHandle( FileHandle,
IopQueryFsOperationAccess[FsInformationClass],
IoFileObjectType,
requestorMode,
(PVOID *) &fileObject,
NULL );
if (!NT_SUCCESS( status )) {
return status;
}
//
// If this open file object represents an open device that was explicitly
// opened for querying the device's attributes, then ensure that the type
// of information class was device information.
//
if ((fileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
FsInformationClass != FileFsDeviceInformation) {
ObDereferenceObject( fileObject );
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// Make a special check here to determine whether this is a synchronous
// I/O operation. If it is, then wait here until the file is owned by
// the current thread. If this is not a (serialized) synchronous I/O
// operation, then allocate and initialize the local event.
//
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
BOOLEAN interrupted;
if (!IopAcquireFastLock( fileObject )) {
status = IopAcquireFileObjectLock( fileObject,
requestorMode,
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
&interrupted );
if (interrupted) {
ObDereferenceObject( fileObject );
return status;
}
}
synchronousIo = TRUE;
} else {
synchronousIo = FALSE;
}
//
// Get the address of the target device object. A special check is made
// here to determine whether this query is for device information. If
// it is, and either:
//
// a) The open was for the device itself, or
//
// b) The open was for a file but this is not a redirected device,
//
// then perform the query operation in-line. That is, do not allocate
// an IRP and call the driver, rather, simply copy the device type and
// characteristics information from the target device object pointed
// to by the device object in the file object (the "real" device object
// in a mass storage device stack).
//
if (FsInformationClass == FileFsDeviceInformation &&
(fileObject->Flags & FO_DIRECT_DEVICE_OPEN ||
fileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM)) {
PFILE_FS_DEVICE_INFORMATION deviceAttributes;
BOOLEAN deviceMounted = FALSE;
//
// This query operation can be performed in-line. Simply copy the
// information directly from the target device object and indicate
// that the operation was successful. Begin, however, by determining
// whether or not the device is mounted. This cannot be done at the
// same time as attempting to touch the user's buffer, as looking at
// the mounted bit occurs at raised IRQL.
//
deviceObject = fileObject->DeviceObject;
if (deviceObject->Vpb) {
deviceMounted = IopGetMountFlag( deviceObject );
}
//
// Copy the characteristics information from the device's object
// into the caller's buffer.
//
deviceAttributes = (PFILE_FS_DEVICE_INFORMATION) FsInformation;
try {
deviceAttributes->DeviceType = deviceObject->DeviceType;
deviceAttributes->Characteristics = deviceObject->Characteristics;
if (deviceMounted) {
deviceAttributes->Characteristics |= FILE_DEVICE_IS_MOUNTED;
}
IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = sizeof( FILE_FS_DEVICE_INFORMATION );
status = STATUS_SUCCESS;
} except( EXCEPTION_EXECUTE_HANDLER ) {
//
// An error occurred attempting to write into one of the caller's
// buffers. Simply indicate that the error occurred, and fall
// through.
//
status = GetExceptionCode();
}
//
// If this operation was performed as synchronous I/O, then release
// the file object lock.
//
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
IopReleaseFileObjectLock( fileObject );
}
//
// Now simply cleanup and return the final status of the operation.
//
ObDereferenceObject( fileObject );
return status;
}
if (FsInformationClass == FileFsDriverPathInformation) {
PFILE_FS_DRIVER_PATH_INFORMATION systemBuffer = NULL;
PFILE_FS_DRIVER_PATH_INFORMATION userBuffer = FsInformation;
try {
systemBuffer = ExAllocatePoolWithQuota( NonPagedPool, Length );
RtlCopyMemory( systemBuffer,
userBuffer,
Length );
status = IopGetDriverPathInformation(fileObject, systemBuffer, Length);
if (!NT_SUCCESS(status)) {
ExRaiseStatus(status);
}
userBuffer->DriverInPath = systemBuffer->DriverInPath;
ExFreePool(systemBuffer);
IoStatusBlock->Status = STATUS_SUCCESS;
IoStatusBlock->Information = sizeof( FILE_FS_DRIVER_PATH_INFORMATION );
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while allocating the intermediary
// system buffer or while copying the caller's data into the
// buffer. Cleanup and return an appropriate error status code.
//
status = GetExceptionCode();
if (systemBuffer) {
ExFreePool(systemBuffer);
}
}
//
// If this operation was performed as synchronous I/O, then release
// the file object lock.
//
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
IopReleaseFileObjectLock( fileObject );
}
ObDereferenceObject( fileObject);
return status;
}
//
// This is either a query that is not for device characteristics
// information, or it is a query for device information, but it is
// a query for a redirected device. Take the long route and actually
// invoke the driver for the target device to get the information.
//
// Set the file object to the Not-Signaled state.
//
KeClearEvent( &fileObject->Event );
//
// Get a pointer to the device object for the target device.
//
deviceObject = IoGetRelatedDeviceObject( fileObject );
//
// If this I/O operation is not being performed as synchronous I/O,
// then allocate an event that will be used to synchronize the
// completion of this operation. That is, this system service is
// a synchronous API being invoked for a file that is opened for
// asynchronous I/O.
//
if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
event = ExAllocatePool( NonPagedPool, sizeof( KEVENT ) );
if (event == NULL) {
ObDereferenceObject( fileObject );
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent( event, SynchronizationEvent, FALSE );
}
//
// Allocate and initialize the I/O Request Packet (IRP) for this
// operation. The allocation is performed with an exception handler
// in case the caller does not have enough quota to allocate the packet.
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
if (!irp) {
//
// An IRP could not be allocated. Cleanup and return an
// appropriate error status code.
//
if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
ExFreePool( event );
}
IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Tail.Overlay.Thread = CurrentThread;
irp->RequestorMode = requestorMode;
//
// Fill in the service independent parameters in the IRP.
//
if (synchronousIo) {
irp->UserEvent = (PKEVENT) NULL;
irp->UserIosb = IoStatusBlock;
} else {
irp->UserEvent = event;
irp->UserIosb = &localIoStatus;
irp->Flags = IRP_SYNCHRONOUS_API;
}
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
//
// Get a pointer to the stack location for the first driver. This will
// be used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_QUERY_VOLUME_INFORMATION;
irpSp->FileObject = fileObject;
//
// Allocate a buffer which should be used to put the information into
// by the driver. This will be copied back to the caller's buffer when
// the service completes. This is done by setting the flag which says
// that this is an input operation.
//
irp->UserBuffer = FsInformation;
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
irp->MdlAddress = (PMDL) NULL;
//
// Allocate the system buffer using an exception handler in case the
// caller doesn't have enough quota remaining.
//
try {
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( NonPagedPool,
Length );
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred attempting to allocate the inter-
// mediary buffer. Cleanup and return with an appropriate error
// status code.
//
IopExceptionCleanup( fileObject,
irp,
(PKEVENT) NULL,
event );
return GetExceptionCode();
}
irp->Flags |= (ULONG) (IRP_BUFFERED_IO |
IRP_DEALLOCATE_BUFFER |
IRP_INPUT_OPERATION |
IRP_DEFER_IO_COMPLETION);
//
// Copy the caller's parameters to the service-specific portion of the
// IRP.
//
irpSp->Parameters.QueryVolume.Length = Length;
irpSp->Parameters.QueryVolume.FsInformationClass = FsInformationClass;
//
// Queue the packet, call the driver, and synchronize appopriately with
// I/O completion.
//
status = IopSynchronousServiceTail( deviceObject,
irp,
fileObject,
TRUE,
requestorMode,
synchronousIo,
OtherTransfer );
//
// If the file for this operation was not opened for synchronous I/O, then
// synchronization of completion of the I/O operation has not yet occurred
// since the allocated event must be used for synchronous APIs on files
// opened for asynchronous I/O. Synchronize the completion of the I/O
// operation now.
//
if (!synchronousIo) {
status = IopSynchronousApiServiceTail( status,
event,
irp,
requestorMode,
&localIoStatus,
IoStatusBlock );
}
return status;
}
NTSTATUS
NtSetVolumeInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FsInformation,
IN ULONG Length,
IN FS_INFORMATION_CLASS FsInformationClass
)
/*++
Routine Description:
This service changes information about the volume "mounted" on the device
specified by the FileHandle parameter. The information to be changed is
in the FsInformation buffer. Its contents are defined by the FsInformation-
Class parameter, whose values may be as follows:
o FileFsLabelInformation
Arguments:
FileHandle - Supplies a handle to the volume whose information should be
changed.
IoStatusBlock - Address of the caller's I/O status block.
FsInformation - Supplies a buffer containing the information which should
be changed on the volume.
Length - Supplies the length, in bytes, of the FsInformation buffer.
FsInformationClass - Specifies the type of information which should be
changed about the volume.
Return Value:
The status returned is the final completion status of the operation.
block.
--*/
{
PIRP irp;
NTSTATUS status;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
PKEVENT event = (PKEVENT) NULL;
KPROCESSOR_MODE requestorMode;
PIO_STACK_LOCATION irpSp;
IO_STATUS_BLOCK localIoStatus;
PFILE_FS_LABEL_INFORMATION labelInformation;
BOOLEAN synchronousIo;
PDEVICE_OBJECT targetDeviceObject;
PETHREAD CurrentThread;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
CurrentThread = PsGetCurrentThread ();
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
if (requestorMode != KernelMode) {
//
// Ensure that the FsInformationClass parameter is legal for setting
// information about the volume.
//
if ((ULONG) FsInformationClass >= FileFsMaximumInformation ||
IopSetFsOperationLength[FsInformationClass] == 0) {
return STATUS_INVALID_INFO_CLASS;
}
//
// Finally, ensure that the supplied buffer is large enough to contain
// the information associated with the specified set operation that is
// to be performed.
//
if (Length < (ULONG) IopSetFsOperationLength[FsInformationClass]) {
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// The caller's access mode is user, so probe each of the arguments
// and capture them as necessary. If any failures occur, the condition
// handler will be invoked to handle them. It will simply cleanup and
// return an access violation status code back to the system service
// dispatcher.
//
try {
//
// The IoStatusBlock parameter must be writeable by the caller.
//
ProbeForWriteIoStatus( IoStatusBlock );
//
// The FsInformation buffer must be readable by the caller.
//
#if defined(_X86_)
ProbeForRead( FsInformation, Length, sizeof( ULONG ) );
#elif defined(_IA64_)
// If we are a wow64 process, follow the X86 rules
if (PsGetCurrentProcessByThread(CurrentThread)->Wow64Process) {
ProbeForRead( FsInformation, Length, sizeof( ULONG ) );
}
else {
ProbeForRead( FsInformation,
Length,
IopQuerySetFsAlignmentRequirement[FsInformationClass] );
}
#else
ProbeForRead( FsInformation,
Length,
IopQuerySetFsAlignmentRequirement[FsInformationClass] );
#endif
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred probing the caller's parameters.
// Simply return an appropriate error status code.
//
return GetExceptionCode();
}
}
//
// There were no blatant errors so far, so reference the file object so
// the target device object can be found. Note that if the handle does
// not refer to a file object, or if the caller does not have the required
// access to the file, then it will fail.
//
status = ObReferenceObjectByHandle( FileHandle,
IopSetFsOperationAccess[FsInformationClass],
IoFileObjectType,
requestorMode,
(PVOID *) &fileObject,
NULL );
if (!NT_SUCCESS( status )) {
return status;
}
//
// Retrieve the device object associated with this file handle.
//
status = IoGetRelatedTargetDevice( fileObject, &targetDeviceObject );
if (NT_SUCCESS( status )) {
//
// The PDO associated with the devnode we got back from
// IoGetRelatedTargetDevice has already been referenced by that
// routine. Store this reference away in the notification entry,
// so we can deref it later when the notification entry is unregistered.
//
ASSERT(targetDeviceObject);
} else {
targetDeviceObject = NULL;
}
//
// Make a special check here to determine whether this is a synchronous
// I/O operation. If it is, then wait here until the file is owned by
// the current thread. if this is not a (serialized) synchronous I/O
// operation, then allocate and initialize the local event.
//
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
BOOLEAN interrupted;
if (!IopAcquireFastLock( fileObject )) {
status = IopAcquireFileObjectLock( fileObject,
requestorMode,
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
&interrupted );
if (interrupted) {
ObDereferenceObject( fileObject );
if (targetDeviceObject != NULL) {
ObDereferenceObject( targetDeviceObject );
}
return status;
}
}
synchronousIo = TRUE;
} else {
//
// This is a synchronous API being invoked for a file that is opened
// for asynchronous I/O. This means that this system service is
// to synchronize the completion of the operation before returning
// to the caller. A local event is used to do this.
//
event = ExAllocatePool( NonPagedPool, sizeof( KEVENT ) );
if (event == NULL) {
ObDereferenceObject( fileObject );
if (targetDeviceObject != NULL) {
ObDereferenceObject( targetDeviceObject );
}
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeEvent( event, SynchronizationEvent, FALSE );
synchronousIo = FALSE;
}
//
// Set the file object to the Not-Signaled state.
//
KeClearEvent( &fileObject->Event );
//
// Get the address of the target device object.
//
deviceObject = IoGetRelatedDeviceObject( fileObject );
//
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
// The allocation is performed with an exception handler in case the
// caller does not have enough quota to allocate the packet.
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
if (!irp) {
//
// An IRP could not be allocated. Cleanup and return an appropriate
// error status code.
//
if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
ExFreePool( event );
}
IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );
if (targetDeviceObject != NULL) {
ObDereferenceObject( targetDeviceObject );
}
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Tail.Overlay.Thread = CurrentThread;
irp->RequestorMode = requestorMode;
//
// Fill in the service independent parameters in the IRP.
//
if (synchronousIo) {
irp->UserEvent = (PKEVENT) NULL;
irp->UserIosb = IoStatusBlock;
} else {
irp->UserEvent = event;
irp->UserIosb = &localIoStatus;
irp->Flags = IRP_SYNCHRONOUS_API;
}
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_SET_VOLUME_INFORMATION;
irpSp->FileObject = fileObject;
//
// Allocate a buffer and copy the information that is to be set on the
// file into it. Also, set the flags so that the completion code will
// properly handle getting rid of the buffer and will not attempt to
// copy data.
//
irp->AssociatedIrp.SystemBuffer = (PVOID) NULL;
irp->MdlAddress = (PMDL) NULL;
try {
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuota( NonPagedPool,
Length );
RtlCopyMemory( irp->AssociatedIrp.SystemBuffer, FsInformation, Length );
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred attempting to allocate the intermediary
// buffer or while copying the caller's data to the buffer. Determine
// what happened, cleanup, and return an appropriate error status
// code.
//
IopExceptionCleanup( fileObject,
irp,
(PKEVENT) NULL,
event );
if (targetDeviceObject != NULL) {
ObDereferenceObject( targetDeviceObject );
}
return GetExceptionCode();
}
//
// If the previous mode was not kernel, check the captured label buffer
// for consistency.
//
if (requestorMode != KernelMode &&
FsInformationClass == FileFsLabelInformation) {
//
// The previous mode was something other than kernel. Check to see
// whether or not the length of the label specified within the label
// structure is consistent with the overall length of the structure
// itself. If not, then cleanup and get out.
//
labelInformation = (PFILE_FS_LABEL_INFORMATION) irp->AssociatedIrp.SystemBuffer;
if ((LONG) labelInformation->VolumeLabelLength < 0 ||
labelInformation->VolumeLabelLength +
FIELD_OFFSET( FILE_FS_LABEL_INFORMATION, VolumeLabel ) > Length) {
IopExceptionCleanup( fileObject,
irp,
(PKEVENT) NULL,
event );
if (targetDeviceObject != NULL) {
ObDereferenceObject( targetDeviceObject );
}
return STATUS_INVALID_PARAMETER;
}
}
irp->Flags |= (ULONG) (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
//
// Copy the caller's parameters to the service-specific portion of the
// IRP.
//
irpSp->Parameters.SetVolume.Length = Length;
irpSp->Parameters.SetVolume.FsInformationClass = FsInformationClass;
//
// Queue the packet, call the driver, and synchronize appopriately with
// I/O completion.
//
status = IopSynchronousServiceTail( deviceObject,
irp,
fileObject,
FALSE,
requestorMode,
synchronousIo,
OtherTransfer );
//
// If the file for this operation was not opened for synchronous I/O, then
// synchronization of completion of the I/O operation has not yet occurred
// since the allocated event must be used for synchronous APIs on files
// opened for asynchronous I/O. Synchronize the completion of the I/O
// operation now.
//
if (!synchronousIo) {
status = IopSynchronousApiServiceTail( status,
event,
irp,
requestorMode,
&localIoStatus,
IoStatusBlock );
}
//
// Notify anyone who cares about the label change
//
if (targetDeviceObject != NULL) {
if (NT_SUCCESS( status )) {
TARGET_DEVICE_CUSTOM_NOTIFICATION ChangeEvent;
ChangeEvent.Version = 1;
ChangeEvent.FileObject = NULL;
ChangeEvent.NameBufferOffset = -1;
ChangeEvent.Size = (USHORT)FIELD_OFFSET( TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer );
RtlCopyMemory( &ChangeEvent.Event, &GUID_IO_VOLUME_CHANGE, sizeof( GUID_IO_VOLUME_CHANGE ));
IoReportTargetDeviceChange( targetDeviceObject, &ChangeEvent );
}
ObDereferenceObject( targetDeviceObject );
}
return status;
}