Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2002 lines
65 KiB

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
sifsctl.c
Abstract:
File system control routines for the single instance store
Authors:
Bill Bolosky, Summer, 1997
Environment:
Kernel mode
Revision History:
--*/
#include "sip.h"
#ifdef ALLOC_PRAGMA
#endif // ALLOC_PRAGMA
typedef struct _SIS_DISMOUNT_CONTEXT {
WORK_QUEUE_ITEM workItem[1];
PDEVICE_EXTENSION deviceExtension;
} SIS_DISMOUNT_CONTEXT, *PSIS_DISMOUNT_CONTEXT;
VOID
SiDismountWork(
IN PVOID parameter)
{
PSIS_DISMOUNT_CONTEXT dismountContext = parameter;
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_DISMOUNT_TRACE_LEVEL,
"SIS: SiDismountWork\n");
#endif // DBG
//
// We're in a system thread, so we don't need to diable APCs before taking the
// GrovelerFileObjectResource.
//
ASSERT(PsIsSystemThread(PsGetCurrentThread()));
ExAcquireResourceExclusiveLite(dismountContext->deviceExtension->GrovelerFileObjectResource, TRUE);
if (NULL != dismountContext->deviceExtension->GrovelerFileHandle) {
NtClose(dismountContext->deviceExtension->GrovelerFileHandle);
dismountContext->deviceExtension->GrovelerFileHandle = NULL;
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_DISMOUNT_TRACE_LEVEL,
"SIS: SiDismountWork closed GrovelerFile handle\n");
#endif // DBG
}
if (NULL != dismountContext->deviceExtension->GrovelerFileObject) {
ObDereferenceObject(dismountContext->deviceExtension->GrovelerFileObject);
dismountContext->deviceExtension->GrovelerFileObject = NULL;
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_DISMOUNT_TRACE_LEVEL,
"SIS: SiDismountWork closed GrovelerFile object\n");
#endif // DBG
}
ExReleaseResourceLite(dismountContext->deviceExtension->GrovelerFileObjectResource);
ExFreePool(dismountContext);
}
NTSTATUS
SiDismountVolumeCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PDEVICE_EXTENSION deviceExtension = Context;
PSIS_DISMOUNT_CONTEXT dismountContext;
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
ASSERT(STATUS_PENDING != Irp->IoStatus.Status);
dismountContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(SIS_DISMOUNT_CONTEXT), ' siS');
if (NULL != dismountContext) {
SIS_MARK_POINT_ULONG(dismountContext);
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_DISMOUNT_TRACE_LEVEL,
"SIS: SiDismountCompletion: queueing dismount work\n");
#endif // DBG
ExInitializeWorkItem(dismountContext->workItem, SiDismountWork, dismountContext);
dismountContext->deviceExtension = deviceExtension;
ExQueueWorkItem(dismountContext->workItem,CriticalWorkQueue);
} else {
//
// Too bad, we'll just dribble it.
//
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: SiDismountCompletion: Unable to allocate dismount context\n");
#endif // DBG
SIS_MARK_POINT();
}
return STATUS_SUCCESS;
}
NTSTATUS
SipDismountVolume(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
Someone is trying a dismount volume request. We can't tell if it's valid, so
trap the completion. If it completes successfully, then we need to clean up our
state.
Arguments:
DeviceObject - Pointer to the device object for this driver.
Irp - Pointer to the request packet representing the FSCTL_DISMOUNT_VOLUME
Return Value:
The function value is the status of the operation.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextIrpSp = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_DISMOUNT_TRACE_LEVEL,
"SIS: SipDismountVolume: called, DO 0x%x, Irp 0x%x\n",DeviceObject, Irp);
#endif // DBG
RtlMoveMemory(nextIrpSp,irpSp,sizeof(IO_STACK_LOCATION));
IoSetCompletionRoutine(
Irp,
SiDismountVolumeCompletion,
DeviceObject->DeviceExtension,
TRUE, // invoke on success
FALSE, // invoke on error
FALSE); // invoke on cancel
return IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
}
NTSTATUS
SiUserSetSISReparsePointCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PKEVENT event = (PKEVENT)Context;
UNREFERENCED_PARAMETER( DeviceObject );
Irp->PendingReturned = FALSE;
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SipUserSetSISReparsePoint(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PREPARSE_DATA_BUFFER reparseBuffer = Irp->AssociatedIrp.SystemBuffer;
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextIrpSp;
ULONG InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
BOOLEAN validReparseData;
CSID CSid;
LINK_INDEX LinkIndex;
LARGE_INTEGER CSFileNtfsId;
LARGE_INTEGER LinkFileNtfsId;
LONGLONG CSFileChecksum;
PSIS_CS_FILE CSFile = NULL;
NTSTATUS status;
ULONG returnedLength;
BOOLEAN prepared = FALSE;
LINK_INDEX newLinkIndex;
KEVENT event[1];
PSIS_PER_LINK perLink = NULL;
FILE_ALL_INFORMATION allInfo[1];
BOOLEAN EligibleForPartialFinalCopy;
KIRQL OldIrql;
PSIS_PER_FILE_OBJECT perFO;
PSIS_SCB scb;
SIS_MARK_POINT();
if (!SipCheckPhase2(deviceExtension)) {
//
// This isn't a SIS enabled volume, or something else bad happened. Just let it go.
//
SIS_MARK_POINT();
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
ASSERT(InputBufferLength >= SIS_REPARSE_DATA_SIZE); // must have been checked by caller
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
//
// This is a SIS reparse point. Figure out whether it's valid.
//
validReparseData = SipIndicesFromReparseBuffer(
reparseBuffer,
&CSid,
&LinkIndex,
&CSFileNtfsId,
&LinkFileNtfsId,
&CSFileChecksum,
&EligibleForPartialFinalCopy,
NULL);
if (SipIsFileObjectSIS(irpSp->FileObject, DeviceObject, FindActive, &perFO, &scb)) {
perLink = scb->PerLink;
//
// This is a SIS file object. If we're setting a reparse point where the CSid and
// CSFile checksum are the same as the current file, assume that it's restore doing
// the set, and just clear the dirty bit and leave the file be. If someone other
// than restore does this, it's harmless to anyone but them.
//
if ((!validReparseData) || (!IsEqualGUID(&CSid, &perLink->CsFile->CSid)) || CSFileChecksum != perLink->CsFile->Checksum) {
//
// The user is trying to set to an invalid reparse point, a different file or
// has a bogus checksum. This isn't implemented.
//
SIS_MARK_POINT_ULONG(scb);
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: SipUserSetSISReparsePoint: unimplemented set\n");
#endif // DBG
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NOT_IMPLEMENTED;
}
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
if ((perLink->Flags &
( SIS_PER_LINK_BACKPOINTER_GONE
| SIS_PER_LINK_FINAL_COPY
| SIS_PER_LINK_FINAL_COPY_DONE
| SIS_PER_LINK_OVERWRITTEN
| SIS_PER_LINK_FILE_DELETED
| SIS_PER_LINK_DELETE_DISPOSITION_SET)) == 0) {
SIS_MARK_POINT_ULONG(scb);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
perLink->Flags &= ~SIS_PER_LINK_DIRTY;
} else {
SIS_MARK_POINT_ULONG(scb);
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: SipUserSetSISReparsePoint: trying to re-set reparse point on file in funny state\n");
#endif // DBG
Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
Irp->IoStatus.Information = 0;
}
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
if (!validReparseData) {
//
// It's not a valid reparse point, so we don't update our backpointers. Just let
// it get set, and we'll delete it if anyone tries to open the resulting file.
//
SIS_MARK_POINT();
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
//
// Rewrite reparse point in the buffer pointed to by the irp to have a new, unused link index
// which prevents problems with files existing on disk with link indices > MaxIndex.
//
status = SipAllocateIndex(deviceExtension,&newLinkIndex);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
newLinkIndex.QuadPart = 0;
newLinkIndex.Check = 0;
}
if (!SipIndicesIntoReparseBuffer(
reparseBuffer,
&CSid,
&newLinkIndex,
&CSFileNtfsId,
&LinkFileNtfsId,
&CSFileChecksum,
EligibleForPartialFinalCopy)) {
status = STATUS_DRIVER_INTERNAL_ERROR;
SIS_MARK_POINT();
goto Error;
}
//
// Get the file information.
//
status = SipQueryInformationFile(
irpSp->FileObject,
DeviceObject,
FileAllInformation,
sizeof(FILE_ALL_INFORMATION),
allInfo,
&returnedLength);
if ((STATUS_BUFFER_OVERFLOW == status) && (returnedLength == sizeof(FILE_ALL_INFORMATION))) {
//
// We expect to get a buffer overflow, because of the file name return. Treat this
// like success.
//
SIS_MARK_POINT();
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
//
// If this is a sparse file and eliginle for partial final copy, then zero out any
// trailing unallocated region.
//
if (EligibleForPartialFinalCopy && (allInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)) {
#define NUM_RANGES_PER_ITERATION 10
FILE_ALLOCATED_RANGE_BUFFER inArb[1];
FILE_ALLOCATED_RANGE_BUFFER outArb[NUM_RANGES_PER_ITERATION];
FILE_ZERO_DATA_INFORMATION zeroData[1];
unsigned allocatedRangesReturned;
for (inArb->FileOffset.QuadPart = 0;
inArb->FileOffset.QuadPart < allInfo->StandardInformation.EndOfFile.QuadPart;
) {
//
// Query the range.
//
inArb->Length.QuadPart = MAXLONGLONG;
status = SipFsControlFile(
irpSp->FileObject,
DeviceObject,
FSCTL_QUERY_ALLOCATED_RANGES,
inArb,
sizeof(FILE_ALLOCATED_RANGE_BUFFER),
outArb,
sizeof(FILE_ALLOCATED_RANGE_BUFFER) * NUM_RANGES_PER_ITERATION,
&returnedLength);
if (!NT_SUCCESS(status)) {
//
// Just skip this part.
//
SIS_MARK_POINT_ULONG(status);
goto VDLExtended;
}
ASSERT(returnedLength % sizeof(FILE_ALLOCATED_RANGE_BUFFER) == 0);
allocatedRangesReturned = returnedLength / sizeof(FILE_ALLOCATED_RANGE_BUFFER);
if (allocatedRangesReturned < NUM_RANGES_PER_ITERATION) {
if ((1 == allocatedRangesReturned) &&
(0 == inArb->FileOffset.QuadPart) &&
(0 == outArb[0].FileOffset.QuadPart) &&
(allInfo->StandardInformation.EndOfFile.QuadPart <= outArb[0].Length.QuadPart) &&
(deviceExtension->FilesystemBytesPerFileRecordSegment.QuadPart >= allInfo->StandardInformation.EndOfFile.QuadPart)) {
//
// This is a special case. This is a small file with a single allocated range extending from
// the start of the file to the end. It's possibly a resident stream, so we FSCTL_SET_ZERO_DATA
// won't necessarily make it go away. We just deal with this by make it not be eligible for partial
// final copy.
//
EligibleForPartialFinalCopy = FALSE;
} else if (allocatedRangesReturned > 0) {
inArb->FileOffset.QuadPart =
outArb[allocatedRangesReturned-1].FileOffset.QuadPart + outArb[allocatedRangesReturned-1].Length.QuadPart;
}
//
// Zero out the remainder of the file, in order to extend ValidDataLength.
//
zeroData->FileOffset = inArb->FileOffset;
zeroData->BeyondFinalZero.QuadPart = MAXLONGLONG;
status = SipFsControlFile(
irpSp->FileObject,
DeviceObject,
FSCTL_SET_ZERO_DATA,
zeroData,
sizeof(FILE_ZERO_DATA_INFORMATION),
NULL, // output buffer
0, // o.b. length
NULL); // returned length
#if DBG
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: SipUserSetSISReparsePoint: unable to zero data, 0x%x\n",status);
}
#endif // DBG
goto VDLExtended;
}
ASSERT(allocatedRangesReturned == NUM_RANGES_PER_ITERATION);
inArb->FileOffset.QuadPart =
outArb[NUM_RANGES_PER_ITERATION-1].FileOffset.QuadPart + outArb[NUM_RANGES_PER_ITERATION-1].Length.QuadPart;
}
#undef NUM_RANGES_PER_ITERATION
}
VDLExtended:
CSFile = SipLookupCSFile(
&CSid,
&CSFileNtfsId,
DeviceObject);
if (NULL == CSFile) {
//
// We couldn't allocate a CSFile, just fail the request.
//
SIS_MARK_POINT();
status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Make sure the common store file is open.
//
status = SipAssureCSFileOpen(CSFile);
if (!NT_SUCCESS(status)) {
//
// It wasn't there or we couldn't get to it for some reason, just let the set proceed.
//
SIS_MARK_POINT_ULONG(status);
SipDereferenceCSFile(CSFile);
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
//
// Check the checksum.
//
if (CSFile->Checksum != CSFileChecksum) {
SIS_MARK_POINT();
//
// The checksum's bogus, so the reparse point isn't good for much. Let the set
// proceed anyway. When the user tries to open this file, we'll delete the reparse
// point.
//
SipDereferenceCSFile(CSFile);
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
//
// Prepare for a refcount change, allocate the new link index,
// and create a new perLink.
//
status = SipPrepareRefcountChangeAndAllocateNewPerLink(
CSFile,
&allInfo->InternalInformation.IndexNumber,
DeviceObject,
&newLinkIndex,
&perLink,
&prepared);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto Error;
}
//
// Construct the new reparse point in the buffer pointed to by the irp.
//
if (!SipIndicesIntoReparseBuffer(
reparseBuffer,
&CSFile->CSid,
&newLinkIndex,
&CSFile->CSFileNtfsId,
&allInfo->InternalInformation.IndexNumber,
&CSFileChecksum,
EligibleForPartialFinalCopy)) {
status = STATUS_DRIVER_INTERNAL_ERROR;
SIS_MARK_POINT();
goto Error;
}
//
// Set an event to synchronize completion.
//
KeInitializeEvent(event, NotificationEvent, FALSE);
//
// Set up the irp
//
nextIrpSp = IoGetNextIrpStackLocation(Irp);
RtlCopyMemory(nextIrpSp, irpSp, sizeof(IO_STACK_LOCATION));
IoSetCompletionRoutine(
Irp,
SiUserSetSISReparsePointCompletion,
event,
TRUE,
TRUE,
TRUE);
IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
status = KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
ASSERT(STATUS_SUCCESS == status);
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
SipCompleteCSRefcountChange(
NULL,
NULL,
CSFile,
FALSE,
TRUE);
} else {
status = SipCompleteCSRefcountChange(
perLink,
&perLink->Index,
CSFile,
TRUE,
TRUE);
if (!NT_SUCCESS(status)) {
//
// We know we just messeded up, so just kick off the volume
// check right away.
//
SIS_MARK_POINT_ULONG(status);
SipCheckVolume(deviceExtension);
}
}
SipDereferencePerLink(perLink);
SipDereferenceCSFile(CSFile);
#if DBG
perLink = NULL;
CSFile = NULL;
#endif // DBG
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
Error:
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
if (prepared) {
ASSERT(NULL != CSFile);
status = SipCompleteCSRefcountChange(
NULL,
NULL,
CSFile,
FALSE,
TRUE);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
}
}
if (NULL != CSFile) {
SipDereferenceCSFile(CSFile);
#if DBG
CSFile = NULL;
#endif // DBG
}
if (NULL != perLink) {
SipDereferencePerLink(perLink);
#if DBG
perLink = NULL;
#endif // DBG
}
return status;
}
NTSTATUS
SipQueryAllocatedRanges(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
This routine implements FSCTL_QUERY_ALLOCATED_RANGES for SIS links. SIS links
that weren't opened FILE_OPEN_REPARSE_POINT look like they're completely allocated
(ie., that they're all data and no holes). This function returns such.
We complete the irp and return the appropriate status.
This code is stolen from NTFS.
Arguments:
DeviceObject - Pointer to the device object for this driver.
Irp - Pointer to the request packet representing the FSCTL_QUERY_ALLOCATED_RANGES.
Return Value:
The function value is the status of the operation.
--*/
{
BOOLEAN validUserBuffer = TRUE;
PFILE_ALLOCATED_RANGE_BUFFER OutputBuffer;
LONGLONG Length, StartingOffset;
NTSTATUS status;
FILE_STANDARD_INFORMATION standardInformation[1];
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
ULONG returnedLength;
ULONG RemainingBytes;
Irp->IoStatus.Information = 0;
//
// Query the file's standard information to get the length.
//
status = SipQueryInformationFile(
IrpSp->FileObject,
DeviceObject,
FileStandardInformation,
sizeof(FILE_STANDARD_INFORMATION),
standardInformation,
&returnedLength);
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto done;
}
ASSERT(returnedLength == sizeof(FILE_STANDARD_INFORMATION));
//
// This is a METHOD_NEITHER buffer, so we have to be careful in touching it.
// Code to check it out stolen from NTFS.
//
try {
if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof(FILE_ALLOCATED_RANGE_BUFFER)) {
status = STATUS_INVALID_PARAMETER;
leave;
}
RemainingBytes = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
OutputBuffer = (PFILE_ALLOCATED_RANGE_BUFFER)SipMapUserBuffer(Irp);
if (NULL == OutputBuffer) {
//
// We couldn't map the user buffer because of resource shortages.
//
SIS_MARK_POINT_ULONG(IrpSp->FileObject);
status = STATUS_INSUFFICIENT_RESOURCES;
goto done;
}
if (KernelMode != Irp->RequestorMode) {
ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
IrpSp->Parameters.FileSystemControl.InputBufferLength,
sizeof( ULONG ));
ProbeForWrite( OutputBuffer, RemainingBytes, sizeof( ULONG ));
} else if (!IsLongAligned( IrpSp->Parameters.FileSystemControl.Type3InputBuffer ) ||
!IsLongAligned( OutputBuffer )) {
validUserBuffer = FALSE;
leave;
}
StartingOffset = ((PFILE_ALLOCATED_RANGE_BUFFER) IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->FileOffset.QuadPart;
Length = ((PFILE_ALLOCATED_RANGE_BUFFER) IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->Length.QuadPart;
//
// Check that the input parameters are valid.
//
if ((Length < 0) ||
(StartingOffset < 0) ||
(Length > MAXLONGLONG - StartingOffset)) {
status = STATUS_INVALID_PARAMETER;
leave;
}
//
// Check that the requested range is within file size
// and has a non-zero length.
//
if (Length == 0) {
SIS_MARK_POINT();
leave;
}
if (StartingOffset >= standardInformation->EndOfFile.QuadPart) {
SIS_MARK_POINT();
leave;
}
if (standardInformation->EndOfFile.QuadPart - StartingOffset < Length) {
Length = standardInformation->EndOfFile.QuadPart - StartingOffset;
}
//
// Show that the entire requested range is allocated.
//
if (RemainingBytes < sizeof( FILE_ALLOCATED_RANGE_BUFFER )) {
status = STATUS_BUFFER_TOO_SMALL;
SIS_MARK_POINT();
} else {
OutputBuffer->FileOffset.QuadPart = StartingOffset;
OutputBuffer->Length.QuadPart = Length;
Irp->IoStatus.Information = sizeof( FILE_ALLOCATED_RANGE_BUFFER );
status = STATUS_SUCCESS;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
validUserBuffer = FALSE;
}
if (!validUserBuffer) {
status = STATUS_INVALID_USER_BUFFER;
}
done:
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
SipMountCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is invoked for the completion of a mount request. This
simply re-syncs back to the dispatch routine so the operation can be
completed.
Arguments:
DeviceObject - Pointer to this driver's device object that was attached to
the file system device object
Irp - Pointer to the IRP that was just completed.
Context - Pointer to the device object allocated during the down path so
we wouldn't have to deal with errors here.
Return Value:
The return value is always STATUS_SUCCESS.
--*/
{
PKEVENT event = Context;
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
//
// If an event routine is defined, signal it
//
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SipLoadFsCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine is invoked for the completion of a LoadFileSystem request.
This simply re-syncs back to the dispatch routine so the operation can be
completed.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the file system
driver load request.
Context - Context parameter for this driver, unused.
Return Value:
The function value for this routine is always success.
--*/
{
PKEVENT event = Context;
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
//
// If an event routine is defined, signal it
//
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SiFsControl (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is invoked whenever an I/O Request Packet (IRP) w/a major
function code of IRP_MJ_FILE_SYSTEM_CONTROL is encountered. For most
IRPs of this type, the packet is simply passed through. However, for
some requests, special processing is required.
Arguments:
DeviceObject - Pointer to the device object for this driver.
Irp - Pointer to the request packet representing the I/O request.
Return Value:
The function value is the status of the operation.
--*/
{
PDEVICE_EXTENSION devExt = DeviceObject->DeviceExtension;
PDEVICE_OBJECT newDeviceObject;
PDEVICE_EXTENSION newDevExt;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
NTSTATUS status;
PIO_STACK_LOCATION nextIrpSp;
PSIS_PER_FILE_OBJECT perFO;
PSIS_SCB scb;
PVPB vpb;
KEVENT waitEvent;
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_FSCONTROL_TRACE_LEVEL,
"SIS: SiFsControl: fo %p, mf %x, code %x\n",
irpSp->FileObject,
irpSp->MinorFunction,
irpSp->Parameters.FileSystemControl.FsControlCode );
#endif
//
// The control device object can't be opened
//
ASSERT(!IS_MY_CONTROL_DEVICE_OBJECT( DeviceObject ));
ASSERT(IS_MY_DEVICE_OBJECT( DeviceObject ));
//
// Begin by determining the minor function code for this file system control
// function.
//
if (irpSp->MinorFunction == IRP_MN_MOUNT_VOLUME) {
SIS_MARK_POINT();
//
// This is a mount request. Create a device object that can be
// attached to the file system's volume device object if this request
// is successful. We allocate this memory now since we can not return
// an error in the completion routine.
//
// Since the device object we are going to attach to has not yet been
// created (it is created by the base file system) we are going to use
// the type of the file system control device object. We are assuming
// that the file system control device object will have the same type
// as the volume device objects associated with it.
//
ASSERT(IS_DESIRED_DEVICE_TYPE(DeviceObject->DeviceType));
status = IoCreateDevice(
FsDriverObject,
sizeof( DEVICE_EXTENSION ),
(PUNICODE_STRING) NULL,
DeviceObject->DeviceType,
0,
FALSE,
&newDeviceObject );
if (NT_SUCCESS( status )) {
//
// We need to save the RealDevice object pointed to by the vpb
// parameter because this vpb may be changed by the underlying
// file system. Both FAT and CDFS may change the VPB address if
// the volume being mounted is one they recognize from a previous
// mount.
//
newDevExt = newDeviceObject->DeviceExtension;
newDevExt->RealDeviceObject = irpSp->Parameters.MountVolume.Vpb->RealDevice;
//
// Get a new IRP stack location and set our mount completion
// routine. Pass along the address of the device object we just
// created as its context.
//
KeInitializeEvent( &waitEvent, SynchronizationEvent, FALSE );
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine(
Irp,
SipMountCompletion,
&waitEvent,
TRUE,
TRUE,
TRUE);
//
// Call the driver
//
status = IoCallDriver( devExt->AttachedToDeviceObject, Irp );
//
// Wait for the completion routine to be called
//
if (STATUS_PENDING == status) {
NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);
ASSERT(localStatus == STATUS_SUCCESS);
}
//
// Get the correct VPB from the real device object saved in our
// device extension. We do this because the VPB in the IRP stack
// may not be the correct VPB when we get here. The underlying
// file system may change VPBs if it detects a volume it has
// mounted previously.
//
vpb = newDevExt->RealDeviceObject->Vpb;
//
// If the operation succeeded and we are not alreayd attached,
// attach to the device object.
//
if (NT_SUCCESS( Irp->IoStatus.Status )) {
//
// Acquire lock so we can atomically test if we area already attached
// and if not, then attach. This prevents a double attach race
// condition.
//
ExAcquireFastMutex( &SisDeviceAttachLock );
//
// The mount succeeded. If we are not already attached,
// attach to the device object. Note: one reason we could
// already be attached is if the underlying file system
// revived a previous mount.
//
if (!SipAttachedToDevice( vpb->DeviceObject )) {
//
// Attach to the new mounted volume. Note that we must
// go through the VPB to locate the file system volume
// device object.
// This routine will cleanup "newDeviceObject" if this
// operation fails.
//
SipAttachToMountedDevice( vpb->DeviceObject,
newDeviceObject,
newDevExt->RealDeviceObject );
} else {
#if DBG
SipCacheDeviceName( newDeviceObject );
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_VOLNAME_TRACE_LEVEL,
"SIS: Mount volume failure for \"%wZ\", already attached\n",
&newDevExt->Name );
#endif
//
// The mount request failed. Cleanup and delete the device
// object we created.
//
SipCleanupDeviceExtension( newDeviceObject );
IoDeleteDevice( newDeviceObject );
}
//
// Release the lock
//
ExReleaseFastMutex( &SisDeviceAttachLock );
} else {
#if DBG
//
// Display what mount failed.
//
SipCacheDeviceName( newDeviceObject );
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_VOLNAME_TRACE_LEVEL,
"SIS: Mount volume failure for \"%wZ\", status=%08x\n",
&newDevExt->Name,
Irp->IoStatus.Status );
#endif
//
// The mount request failed. Cleanup and delete the device
// object we created.
//
SipCleanupDeviceExtension( newDeviceObject );
IoDeleteDevice( newDeviceObject );
}
//
// Continue processing the operation
//
status = Irp->IoStatus.Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
} else {
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: Error creating volume device object, status=%08x\n",
status );
#endif
//
// Something went wrong so this volume cannot be filtered. Simply
// allow the system to continue working normally, if possible.
//
IoSkipCurrentIrpStackLocation( Irp );
}
} else if (irpSp->MinorFunction == IRP_MN_LOAD_FILE_SYSTEM) {
//
// This is a "load file system" request being sent to a file system
// recognizer device object.
//
#if DBG
SipCacheDeviceName( DeviceObject );
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_VOLNAME_TRACE_LEVEL,
"SIS: Loading File System, Detaching from \"%wZ\"\n",
&devExt->Name );
#endif
//
// Set a completion routine so we can delete the device object when
// the detach is complete.
//
KeInitializeEvent( &waitEvent, SynchronizationEvent, FALSE );
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine(
Irp,
SipLoadFsCompletion,
&waitEvent,
TRUE,
TRUE,
TRUE );
//
// Detach from the recognizer device.
//
IoDetachDevice( devExt->AttachedToDeviceObject );
//
// Call the driver
//
status = IoCallDriver( devExt->AttachedToDeviceObject, Irp );
//
// Wait for the completion routine to be called
//
if (STATUS_PENDING == status) {
NTSTATUS localStatus = KeWaitForSingleObject(&waitEvent, Executive, KernelMode, FALSE, NULL);
ASSERT(localStatus == STATUS_SUCCESS);
}
#if DBG
//
// Display the name if requested
//
SipCacheDeviceName( DeviceObject );
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_VOLNAME_TRACE_LEVEL,
"SIS: Detaching from recognizer \"%wZ\", status=%08x\n",
&devExt->Name,
Irp->IoStatus.Status );
#endif
//
// Check status of the operation
//
if (!NT_SUCCESS( Irp->IoStatus.Status )) {
//
// The load was not successful. Simply reattach to the recognizer
// driver in case it ever figures out how to get the driver loaded
// on a subsequent call.
//
status = IoAttachDeviceToDeviceStackSafe( DeviceObject,
devExt->AttachedToDeviceObject,
&devExt->AttachedToDeviceObject );
ASSERT(STATUS_SUCCESS == status);
} else {
//
// The load was successful, delete the Device object attached to the
// recognizer.
//
SipCleanupDeviceExtension( DeviceObject );
IoDeleteDevice( DeviceObject );
}
//
// Continue processing the operation
//
status = Irp->IoStatus.Status;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return status;
} else if (IRP_MN_USER_FS_REQUEST == irpSp->MinorFunction &&
SipIsFileObjectSIS(irpSp->FileObject,DeviceObject,FindActive,&perFO,&scb)) {
SIS_MARK_POINT_ULONG(scb);
SIS_MARK_POINT_ULONG(irpSp->Parameters.FileSystemControl.FsControlCode);
//
// This is a big switch of all of the known fsctl calls. Most of these calls just get
// passed through on the link file, but we explicity list them to indicate that we put
// some thought into the particular call and determined that passing it through is
// appropriate. In the checked build, we generate a DbgPrint for unknown fsctl calls,
// then pass them through on the link file.
//
switch (irpSp->Parameters.FileSystemControl.FsControlCode) {
//
// Fsctl calls 0-5
//
// oplock calls all get passed through.
//
case FSCTL_REQUEST_OPLOCK_LEVEL_1:
case FSCTL_REQUEST_OPLOCK_LEVEL_2:
case FSCTL_REQUEST_BATCH_OPLOCK:
case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
case FSCTL_OPLOCK_BREAK_NOTIFY:
goto PassThrough;
//
// Fsctl call 6
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_LOCK_VOLUME:
goto PassThrough;
//
// Fsctl call 7
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_UNLOCK_VOLUME:
goto PassThrough;
//
// Fsctl call 8
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_DISMOUNT_VOLUME:
goto PassThrough;
//
// Fsctl call 9 is decommissioned.
//
//
// Fsctl call 10
//
// This call only looks at the volume on which the file is located, it doesn't
// depend on the particular file. Pass it through on the link file.
//
case FSCTL_IS_VOLUME_MOUNTED:
goto PassThrough;
//
// Fsctl call 11
//
// Ntfs doesn't even look at the parameters, it just succeeds the request.
//
case FSCTL_IS_PATHNAME_VALID:
goto PassThrough;
//
// Fsctl call 12
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_MARK_VOLUME_DIRTY:
goto PassThrough;
//
// Fsctl call 13 is decommissioned.
//
//
// Fsctl call 14
//
// This is valid only on paging files and only in kernel mode. Pass through and let NTFS
// fail or assert.
//
case FSCTL_QUERY_RETRIEVAL_POINTERS:
goto PassThrough;
//
// Fsctl calls 15 and 16
//
// The compression state of the link file is independent
// of the compression state of the CS file (which preferably
// is compressed). Pass through.
//
case FSCTL_GET_COMPRESSION:
case FSCTL_SET_COMPRESSION:
goto PassThrough;
//
// Fsctl calls 17 and 18 are decommissioned.
//
//
// Fsctl call 19
//
// This is disconcerting--ntfs treats system hives specially.
// Basically, it works hard to keep them consistent across crashes.
// It's not such a good idea to do this with a SIS file, since we're
// not going to be all that great with user data across a crash. However,
// given that we've gotten here, just go for it.
//
case FSCTL_MARK_AS_SYSTEM_HIVE:
ASSERT(!"SIS: SiFsControl: Someone called FSCTL_MARK_AS_SYSTEM_HIVE on a SIS file!\n");
goto PassThrough;
//
// Fsctl call 20
//
// oplock calls all get passed through.
//
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
goto PassThrough;
//
// Fsctl call 21
//
// NTFS doesn't even mention this fsctl. We'll let it fail it.
//
case FSCTL_INVALIDATE_VOLUMES:
goto PassThrough;
//
// Fsctl call 22
//
// NTFS doesn't even mention this fsctl. We'll let it fail it.
//
case FSCTL_QUERY_FAT_BPB:
goto PassThrough;
//
// Fsctl call 23
//
// oplock calls all get passed through.
//
case FSCTL_REQUEST_FILTER_OPLOCK:
goto PassThrough;
//
// Fsctl call 24
//
// This call only looks at the volume on which the file is located, it doesn't
// depend on the particular file. Pass it through on the link file.
//
case FSCTL_FILESYSTEM_GET_STATISTICS:
goto PassThrough;
//
// Fsctl call 25
//
// This call only looks at the volume on which the file is located, it doesn't
// depend on the particular file. Pass it through on the link file.
//
case FSCTL_GET_NTFS_VOLUME_DATA:
goto PassThrough;
//
// Fsctl call 26
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_GET_NTFS_FILE_RECORD:
goto PassThrough;
//
// Fsctl call 27
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_GET_VOLUME_BITMAP:
goto PassThrough;
//
// Fsctl call 28
//
// This returns file cluster allocation information.
// If opened reparse, pass through. If not, then send to
// where the data is.
//
case FSCTL_GET_RETRIEVAL_POINTERS: {
BOOLEAN openedAsReparse;
KIRQL OldIrql;
BOOLEAN dirty;
KeAcquireSpinLock(perFO->SpinLock, &OldIrql);
openedAsReparse = (perFO->Flags & SIS_PER_FO_OPEN_REPARSE) ? TRUE : FALSE;
KeReleaseSpinLock(perFO->SpinLock, OldIrql);
if (openedAsReparse) {
//
// The user opened this file FILE_OPEN_REPARSE_POINT, so tell the truth
// about the link file.
//
goto PassThrough;
}
KeAcquireSpinLock(scb->PerLink->SpinLock, &OldIrql);
dirty = (scb->PerLink->Flags & SIS_PER_LINK_DIRTY) ? TRUE : FALSE;
KeReleaseSpinLock(scb->PerLink->SpinLock, OldIrql);
//
// Just because the per-link dirty bit isn't set doesn't mean that the
// file's totally clean. Check the scb bits.
//
if (!dirty) {
SipAcquireScb(scb);
if (scb->Flags & SIS_SCB_BACKING_FILE_OPENED_DIRTY) {
dirty = TRUE;
}
SipReleaseScb(scb);
}
if (dirty) {
//
// We should look at the ranges queried and split things up, much like we do with
// reads that span dirty/clean boundaries.
//
// NTRAID#65190-2000/03/10-nealch Handle FSCTL_GET_RETRIEVAL_POINTERS for "dirtied" sis files.
//
SIS_MARK_POINT_ULONG(scb);
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: SiFsControl: FSCTL_GET_RETRIEVAL_POINTERS: called on dirty file, returning STATUS_NOT_IMPLEMENTED\n");
#endif // DBG
status = STATUS_NOT_IMPLEMENTED;
goto CompleteWithStatus;
}
//
// Just send this to the common store file.
//
goto SendToCSFile;
}
//
// Fsctl call 29
//
// This is called on a volume handle, but a file handle
// is passed in the input buffer. It moves a range of the
// file to a specified location on the volume. We just pass it through
// regardless; trying to move unallocated regions of a link file is
// meaningless, and trying to move allocated regions will do the
// right thing. To move the common store file, it can be called with
// a CS file handle.
//
case FSCTL_MOVE_FILE:
goto PassThrough;
//
// Fsctl call 30
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_IS_VOLUME_DIRTY:
goto PassThrough;
//
// Fsctl call 32
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_ALLOW_EXTENDED_DASD_IO:
goto PassThrough;
//
// Fsctl call 33 is decommissioned.
//
//
// Fsctl call 35
//
// Directory only - pass through and let NTFS fail.
//
case FSCTL_FIND_FILES_BY_SID:
goto PassThrough;
//
// Fsctl call 36 is decommissioned.
//
//
// Fsctl call 37 is decommissioned.
//
//
// Fsctls 38-40.
//
// Pass through. Object ID's are similar to file ID's, but are user assigned.
//
case FSCTL_SET_OBJECT_ID:
case FSCTL_GET_OBJECT_ID:
case FSCTL_DELETE_OBJECT_ID:
goto PassThrough;
//
// Fsctl call 41
//
// We can have only one reparse point on a file, and SIS is using it. We should
// probably COW this file, but for now just disallow this, except in the case
// where it's a SIS reparse point being set, in which case we forward the request to
// SipUserSetSISReparsePoint.
//
case FSCTL_SET_REPARSE_POINT: {
PREPARSE_DATA_BUFFER reparseBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
if ((NULL == reparseBuffer) ||
(InputBufferLength < SIS_REPARSE_DATA_SIZE)) {
SIS_MARK_POINT_ULONG(InputBufferLength);
status = STATUS_INVALID_PARAMETER;
goto CompleteWithStatus;
}
if (IO_REPARSE_TAG_SIS != reparseBuffer->ReparseTag) {
status = STATUS_INVALID_PARAMETER;
goto CompleteWithStatus;
}
status = SipUserSetSISReparsePoint(DeviceObject, Irp);
SIS_MARK_POINT_ULONG(status);
return status;
}
//
// Fsctl call 42
//
// Just let the user read the SIS reparse point.
//
case FSCTL_GET_REPARSE_POINT:
goto PassThrough;
//
// Fsctl call 43
//
// Disallow deleting SIS reparse points directly.
//
case FSCTL_DELETE_REPARSE_POINT:
status = STATUS_ACCESS_DENIED;
goto CompleteWithStatus;
//
// Fsctl call 44
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_ENUM_USN_DATA:
goto PassThrough;
//
// Fsctl call 45
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_SECURITY_ID_CHECK:
goto PassThrough;
//
// Fsctl call 46
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_READ_USN_JOURNAL:
goto PassThrough;
//
// Fsctls 47 and 48
//
// Pass through. Object ID's are similar to file ID's, but are user assigned.
//
case FSCTL_SET_OBJECT_ID_EXTENDED:
case FSCTL_CREATE_OR_GET_OBJECT_ID:
goto PassThrough;
//
// Fsctl call 49
//
// SIS link files are already sparse.
//
case FSCTL_SET_SPARSE:
goto PassThrough;
//
// Fsctl call 50
//
// This is only partially implemented, for the case where the user opened the
// file FILE_OPEN_REPARSE_POINT.
//
case FSCTL_SET_ZERO_DATA: {
BOOLEAN openedAsReparse;
BOOLEAN openedDirty;
KIRQL OldIrql;
//
// Check to see if the file is opened FILE_OPEN_REPARSE_POINT, and fail the request if not.
//
KeAcquireSpinLock(perFO->SpinLock, &OldIrql);
openedAsReparse = (perFO->Flags & SIS_PER_FO_OPEN_REPARSE) ? TRUE : FALSE;
KeReleaseSpinLock(perFO->SpinLock, OldIrql);
if (!openedAsReparse) {
status = STATUS_NOT_IMPLEMENTED;
goto CompleteWithStatus;
}
//
// Verify that this file wasn't opened dirty.
//
SipAcquireScb(scb);
openedDirty = (scb->Flags & SIS_SCB_BACKING_FILE_OPENED_DIRTY) ? TRUE : FALSE;
SipReleaseScb(scb);
if (openedDirty) {
SIS_MARK_POINT();
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SIS: tried FSCTL_SET_ZERO_DATA on file that was opened dirty. Failing it.\n");
#endif // DBG
status = STATUS_NOT_IMPLEMENTED;
goto CompleteWithStatus;
}
goto PassThrough;
}
//
// Fsctl call 51
//
// Depends on whether the file is opened FILE_OPEN_REPARSE. Implemented in its own
// function.
//
case FSCTL_QUERY_ALLOCATED_RANGES: {
BOOLEAN openedAsReparse;
KIRQL OldIrql;
KeAcquireSpinLock(perFO->SpinLock, &OldIrql);
openedAsReparse = (perFO->Flags & SIS_PER_FO_OPEN_REPARSE) ? TRUE : FALSE;
KeReleaseSpinLock(perFO->SpinLock, OldIrql);
if (openedAsReparse) {
//
// This file was opened as a reparse point, so we just pass it
// through and let the real set of allocated ranges show through.
//
goto PassThrough;
} else {
//
// This was a normal open, so call our special routine that will
// show the file as a single, allocated chunk.
//
return SipQueryAllocatedRanges(DeviceObject,Irp);
}
}
//
// Fsctl call 52 obsolete
//
//
// Fsctl calls 53-56
//
// Encrypting a file results in the file being completely
// overwritten. SIS files, therefore, will simply COW back
// to normal if they're encrypted.
//
case FSCTL_SET_ENCRYPTION:
case FSCTL_ENCRYPTION_FSCTL_IO:
case FSCTL_WRITE_RAW_ENCRYPTED:
case FSCTL_READ_RAW_ENCRYPTED:
goto PassThrough;
//
// Fsctl call 57
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_CREATE_USN_JOURNAL:
goto PassThrough;
//
// Fsctl call 58
//
// Returns USN data for a file.
//
case FSCTL_READ_FILE_USN_DATA:
goto PassThrough;
//
// Fsctl call 59
//
// This call writes a USN record as if the file were closed, and posts
// any USN updates that were pending a close for this file. Pass it
// through on the link file.
//
case FSCTL_WRITE_USN_CLOSE_RECORD:
goto PassThrough;
//
// Fsctl call 60
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_EXTEND_VOLUME:
goto PassThrough;
//
// Fsctl call 61
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_QUERY_USN_JOURNAL:
goto PassThrough;
//
// Fsctl call 62
//
// Volume only - pass through and let NTFS fail.
//
case FSCTL_DELETE_USN_JOURNAL:
goto PassThrough;
//
// Fsctl call 63
//
// This sets some bits in the CCB related to USN processing
// for the file. These bits don't appear to be read by anything
// in ntfs.
//
case FSCTL_MARK_HANDLE:
goto PassThrough;
//
// Fsctl call 64
//
// Our very own copyfile request.
//
case FSCTL_SIS_COPYFILE:
return SipFsCopyFile(DeviceObject,Irp);
//
// Fsctl call 65
//
// The groveler fsctl. This is valid only on \SIS Common Store\GrovelerFile, which
// can never be a SIS link. Fail the request.
//
case FSCTL_SIS_LINK_FILES:
status = STATUS_ACCESS_DENIED;
goto CompleteWithStatus;
//
// Fsctl call 66
//
// Something related to HSM. Not sure what to do with this; just pass it through.
//
case FSCTL_HSM_MSG:
goto PassThrough;
//
// Handle everything else
//
default:
#if DBG
DbgPrintEx( DPFLTR_SIS_ID, DPFLTR_ERROR_LEVEL,
"SiFsControl with unknown code 0x%x\n",irpSp->Parameters.FileSystemControl.FsControlCode);
if (BJBDebug & 0x400) {
DbgBreakPoint();
}
#endif // DBG
goto PassThrough;
}
} else if (IRP_MN_USER_FS_REQUEST == irpSp->MinorFunction &&
FSCTL_SIS_COPYFILE == irpSp->Parameters.FileSystemControl.FsControlCode) {
return SipFsCopyFile(DeviceObject,Irp);
} else if (IRP_MN_USER_FS_REQUEST == irpSp->MinorFunction &&
FSCTL_SIS_LINK_FILES == irpSp->Parameters.FileSystemControl.FsControlCode) {
return SipLinkFiles(DeviceObject, Irp);
} else if (IRP_MN_USER_FS_REQUEST == irpSp->MinorFunction &&
FSCTL_DISMOUNT_VOLUME == irpSp->Parameters.FileSystemControl.FsControlCode) {
return SipDismountVolume(DeviceObject, Irp);
} else if (IRP_MN_USER_FS_REQUEST == irpSp->MinorFunction &&
FSCTL_SET_REPARSE_POINT == irpSp->Parameters.FileSystemControl.FsControlCode) {
PREPARSE_DATA_BUFFER reparseBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
//
// Handle a user set of a SIS reparse point.
//
if ((NULL == reparseBuffer) ||
(InputBufferLength < SIS_REPARSE_DATA_SIZE) ||
(IO_REPARSE_TAG_SIS != reparseBuffer->ReparseTag)) {
//
// This isn't a valid SIS reparse point being set, just pass the call through.
//
goto PassThrough;
}
status = SipUserSetSISReparsePoint(DeviceObject, Irp);
return status;
} else {
//
// Simply pass this file system control request through, we don't need a callback
//
PassThrough:
IoSkipCurrentIrpStackLocation( Irp );
}
//
// Any special processing has been completed, so simply pass the request
// along to the next driver.
//
return IoCallDriver( devExt->AttachedToDeviceObject, Irp );
/////////////////////////////////////////////////////////////////////////////
// Handle status cases
/////////////////////////////////////////////////////////////////////////////
CompleteWithStatus:
SIS_MARK_POINT_ULONG(status);
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
SendToCSFile:
IoCopyCurrentIrpStackLocationToNext( Irp );
nextIrpSp = IoGetNextIrpStackLocation(Irp);
nextIrpSp->FileObject = scb->PerLink->CsFile->UnderlyingFileObject;
IoSetCompletionRoutine(
Irp,
NULL,
NULL,
FALSE,
FALSE,
FALSE);
return IoCallDriver( devExt->AttachedToDeviceObject, Irp );
}