966 lines
30 KiB
C
966 lines
30 KiB
C
/*++
|
||
|
||
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;
|
||
}
|