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.
 
 
 
 
 
 

1472 lines
39 KiB

/*++
Copyright (c) 1997, 1998 Microsoft Corporation
Module Name:
siinfo.c
Abstract:
Set/Query info routines for the single instance store
Authors:
Bill Bolosky, Summer, 1997
Environment:
Kernel mode
Revision History:
--*/
#include "sip.h"
//
// The parameters for a SipRenameOverCheck call.
//
//
typedef struct _SIS_CHECK_OVERWRITE_REQUEST {
//
// The WORK_QUEUE_ITEM needed to get the request posted.
//
WORK_QUEUE_ITEM workItem[1];
//
// An event to set to signify completion
//
KEVENT event[1];
//
// The per link for the target file.
//
PSIS_PER_LINK perLink;
//
// The file ID for the target file.
//
PFILE_INTERNAL_INFORMATION internalInfo;
//
// The DeviceObject on which we were called.
//
PDEVICE_OBJECT DeviceObject;
//
// The thread that called SipPrepareCSRefcountChange
//
ERESOURCE_THREAD thread;
} SIS_CHECK_OVERWRITE_REQUEST, *PSIS_CHECK_OVERWRITE_REQUEST;
VOID
SipRenameOverCheck(
PVOID parameter)
/*++
Routine Description:
Someone did a replace-if-exists NtSetInformationFile call, the target was
a SIS link, and NTFS completed the request successfully. The caller posted
to a worker thread to make the final determination if the file is really
gone, and this function is the worker routine that makes that check.
We open the target SIS file by ID, and if it's still there we check to see
if it's still the same SIS file. If it is, we abort the refcount update,
otherwise we complete it.
Arguments:
parameter - a PSIS_CHECK_OVERWRITE_REQUEST. See the structure definition for
a description of the fields.
Return Value:
void
--*/
{
PSIS_CHECK_OVERWRITE_REQUEST checkRequest = parameter;
PDEVICE_OBJECT DeviceObject = checkRequest->DeviceObject;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
IO_STATUS_BLOCK Iosb[1];
NTSTATUS status;
OBJECT_ATTRIBUTES Obja[1];
UNICODE_STRING fileIdString[1];
HANDLE dstFileHandle = NULL;
PFILE_OBJECT dstFileObject = NULL;
BOOLEAN fileGone = FALSE;
PSIS_PER_FILE_OBJECT perFO;
PSIS_SCB scb;
KIRQL OldIrql;
fileIdString->Length = fileIdString->MaximumLength = sizeof(LARGE_INTEGER);
fileIdString->Buffer = (PWCHAR)&checkRequest->internalInfo->IndexNumber;
InitializeObjectAttributes(
Obja,
fileIdString,
OBJ_CASE_INSENSITIVE,
deviceExtension->GrovelerFileHandle,
NULL);
status = NtCreateFile(
&dstFileHandle,
0,
Obja,
Iosb,
NULL, // allocation size
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL, // EA buffer
0); // EA length
if (!NT_SUCCESS(status)) {
//
// We couldn't open the file. open-by-id creates fail with invalid parameter
// when there is no file with that id. If that's what happened, then the file's
// gone and we can delete the backpointer. If it failed for some other reason,
// then we'll err on the side of conservatism and leave it be.
//
SIS_MARK_POINT_ULONG(status);
if ((STATUS_INVALID_PARAMETER == status) ||
(STATUS_OBJECT_NAME_NOT_FOUND == status) ||
(STATUS_OBJECT_PATH_NOT_FOUND == status)) {
fileGone = TRUE;
}
goto done;
}
//
// We opened a file with the right file ID. See if it's still a SIS file.
//
status = ObReferenceObjectByHandle(
dstFileHandle,
0,
*IoFileObjectType,
KernelMode,
&dstFileObject,
NULL);
if (!NT_SUCCESS(status)) {
//
// The file's there, but for some reason we can't access the file object.
// Be conservative and assume that it's still the link.
//
SIS_MARK_POINT_ULONG(status);
goto done;
}
if (!SipIsFileObjectSIS(dstFileObject, DeviceObject, FindActive, &perFO, &scb)) {
//
// The file exists, but it's not a SIS file.
//
SIS_MARK_POINT();
fileGone = TRUE;
goto done;
}
//
// The file exists, and it's a SIS file. See if it's a reference to the same file,
// or to a different one. We don't have to worry about it's being a new reference to the
// same file, because we hold the refcount update resource for the common store file.
//
if (scb->PerLink != checkRequest->perLink) {
SIS_MARK_POINT();
fileGone = TRUE;
} else {
//
// It's still there. Presumably someone renamed it before the rename that we're
// tracking could blow it away.
//
}
done:
SIS_MARK_POINT_ULONG(checkRequest->perLink->CsFile);
if (fileGone) {
KeAcquireSpinLock(checkRequest->perLink->SpinLock, &OldIrql);
checkRequest->perLink->Flags |= SIS_PER_LINK_FILE_DELETED;
KeReleaseSpinLock(checkRequest->perLink->SpinLock, OldIrql);
}
SipCompleteCSRefcountChangeForThread(
checkRequest->perLink,
&checkRequest->perLink->Index,
checkRequest->perLink->CsFile,
fileGone,
FALSE,
checkRequest->thread);
SipDereferencePerLink(checkRequest->perLink);
if (NULL != dstFileObject) {
ObDereferenceObject(dstFileObject);
}
if (NULL != dstFileHandle) {
NtClose(dstFileHandle);
}
KeSetEvent(checkRequest->event, IO_NO_INCREMENT, FALSE);
return;
}
NTSTATUS
SiRenameOverCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID context)
/*++
Routine Description:
An IRP completion routine for a rename call with a SIS link as a target.
This function just resynchronizes with the calling thread by clearing
PendingReturned and setting an event.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing rename request.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED
--*/
{
PKEVENT event = (PKEVENT)context;
UNREFERENCED_PARAMETER( DeviceObject );
Irp->PendingReturned = FALSE;
KeSetEvent(event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
SipReplaceIfExistsRename(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
/*++
Routine Description:
Someone did a replace-if-exists NtSetInformationFile call. We need to
figure out if the target is a SIS link, and if so remove the backpointer
for the file. This function does about half of the work, and then posts
to a worker thread (SipRenameOverCheck) to do the final check to see if
the SIS file was really overwritten.
It is the responsibility of this function to complete the irp.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing rename request.
Return Value:
The status of the request
--*/
{
HANDLE dstFileHandle = NULL;
OBJECT_ATTRIBUTES Obja[1];
PFILE_RENAME_INFORMATION renameInfo = Irp->AssociatedIrp.SystemBuffer;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextIrpSp;
UNICODE_STRING dstFileName[1];
NTSTATUS status;
IO_STATUS_BLOCK Iosb[1];
PFILE_OBJECT dstFileObject = NULL;
PSIS_PER_FILE_OBJECT perFO;
PSIS_SCB scb;
FILE_INTERNAL_INFORMATION internalInfo[1];
PSIS_PER_LINK perLink;
KEVENT event[1];
SIS_CHECK_OVERWRITE_REQUEST checkRequest[1];
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
ASSERT(IRP_MJ_SET_INFORMATION == irpSp->MajorFunction);
ASSERT(renameInfo->ReplaceIfExists);
//
// The basic strategy here is similar to what we do in the overwrite/supersede open
// case: we figure out if the file is a SIS file, and if so which one, let the
// rename proceed and then see if the suspected target is gone. Like overwrite/supersede,
// we're subject to a race in the case where someone renames a SIS link under the
// destination after we do our local check. If that race goes the wrong way, we'll
// lose the refcount decrement. This won't ever result in user data loss, but we
// won't be able to reclaim the common store file until we do a volume check. C'est la vie.
//
//
// The first step is to open the target file and see if it is a SIS link.
//
//
// We have to be careful in dealing with the string lengths in the renameInfo buffer,
// because they have not been checked anywhere.
//
if (renameInfo->FileNameLength + FIELD_OFFSET(FILE_RENAME_INFORMATION,FileName) >
irpSp->Parameters.SetFile.Length) {
//
// It's bogus, just let NTFS deal with it.
//
SIS_MARK_POINT();
goto PassThrough;
}
//
// The length is OK, so build up an object attributes for this file.
//
dstFileName->Length = (USHORT)renameInfo->FileNameLength;
dstFileName->MaximumLength = (USHORT)renameInfo->FileNameLength;
dstFileName->Buffer = renameInfo->FileName;
InitializeObjectAttributes(
Obja,
dstFileName,
OBJ_CASE_INSENSITIVE,
renameInfo->RootDirectory,
NULL);
//
// Open the file. It's somewhat unusual to open a file with kernel privs in the
// user context, but it's OK in this case for the following reasons:
// 1) We're opening the file for no access, so the user can't do anything
// aisde from close the handle.
// 2) If the user does close the handle before we reference it, the worst
// that happens is that we miss the loss of a SIS link, which could happen
// via the rename race anyway.
//
status = ZwCreateFile(
&dstFileHandle,
0, // desired access; avoid sharing violations
Obja,
Iosb,
NULL, // allocation size
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE,
NULL, // EA buffer
0); // EA length
if (!NT_SUCCESS(status)) {
//
// For whatever reason, we couldn't open the file. It probably doesn't exist.
// Just pass the request through.
//
SIS_MARK_POINT_ULONG(status);
goto PassThrough;
}
status = ObReferenceObjectByHandle(
dstFileHandle,
0,
*IoFileObjectType,
KernelMode,
&dstFileObject,
NULL); // handle info
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto PassThrough;
}
//
// See if this file is on the same device as we're being called on.
//
if (IoGetRelatedDeviceObject(dstFileObject) !=
IoGetRelatedDeviceObject(irpSp->FileObject)) {
//
// They're not. This call will most likely fail in NTFS since cross-volume
// renames aren't supported at the NT interface level. We'll just pass it
// through.
//
SIS_MARK_POINT();
goto PassThrough;
}
if (!SipCheckPhase2(deviceExtension)) {
SIS_MARK_POINT();
goto PassThrough;
}
if (!SipIsFileObjectSIS(dstFileObject, DeviceObject, FindActive, &perFO, &scb)) {
//
// It exists, but it's not a SIS file object. Pass through.
//
SIS_MARK_POINT();
goto PassThrough;
}
perLink = scb->PerLink;
//
// This is a rename-over with a destination that's a SIS file object. Get the file
// id of the destination file, prepare a refcount change, and close our handle to the
// file.
//
status = SipQueryInformationFile(
dstFileObject,
DeviceObject,
FileInternalInformation,
sizeof(FILE_INTERNAL_INFORMATION),
internalInfo,
NULL); // returnedLength
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto PassThrough;
}
status = SipPrepareCSRefcountChange(
perLink->CsFile,
&perLink->Index,
&internalInfo->IndexNumber,
SIS_REFCOUNT_UPDATE_LINK_DELETED); // rename-over destroys the destination file ID, so it's DELETED, not OVERWRITTEN
if (!NT_SUCCESS(status)) {
SIS_MARK_POINT_ULONG(status);
goto PassThrough;
}
SipReferencePerLink(perLink);
ObDereferenceObject(dstFileObject);
ZwClose(dstFileHandle);
dstFileObject = NULL;
dstFileHandle = NULL;
//
// Now call NTFS and resynchronize with the irp when it completes.
//
KeInitializeEvent(event,NotificationEvent,FALSE);
nextIrpSp = IoGetNextIrpStackLocation(Irp);
RtlMoveMemory(nextIrpSp, irpSp, sizeof (IO_STACK_LOCATION));
IoSetCompletionRoutine( Irp,
SiRenameOverCompletion,
event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
if (STATUS_PENDING == status) {
KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
}
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
//
// It failed, so the SIS link target wasn't destroyed.
//
SIS_MARK_POINT_ULONG(status);
goto NoOverwrite;
}
//
// The rename completed successfully. See if the destination file is still there.
// Unfortunately, we have to post to do this, because we can't open the file by
// name, lest someone rename it away from the rename-over that just completed (this
// is a different race than the one that's left open, and would cause the backpointer
// to go away when the file still exists, which is a much worse problem). In order
// to open it by ID, we need a handle to a file on this volume, which we have in the
// device extension, but it's in the system process context.
//
// We need to take an extra reference to the PerLink before handing off to the
// worker thread. This is because we need to call SipHandoffBackpointerResource
// after posting our work request, but in posting the work request we lose our
// original reference to the per link, so there's no way to guarantee that the
// csfile still exists after the post happens.
//
SipReferencePerLink(perLink);
KeInitializeEvent(checkRequest->event, NotificationEvent, FALSE);
checkRequest->perLink = perLink;
checkRequest->internalInfo = internalInfo;
checkRequest->DeviceObject = DeviceObject;
checkRequest->thread = ExGetCurrentResourceThread();
ExInitializeWorkItem(
checkRequest->workItem,
SipRenameOverCheck,
checkRequest);
ExQueueWorkItem(
checkRequest->workItem,
CriticalWorkQueue);
//
// The worker thread now will complete the refcount change, so we
// need to say that we've handed off, and also drop the extra perLink
// reference that we took.
//
SipHandoffBackpointerResource(perLink->CsFile);
SipDereferencePerLink(perLink);
KeWaitForSingleObject(checkRequest->event, Executive, KernelMode, FALSE, NULL);
//
// Now it's all done, just complete the irp.
//
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
PassThrough:
//
// Something went wrong (or this just isn't a SIS file or doesn't exist or whatever).
// Clean up and pass the request down.
//
if (NULL != dstFileObject) {
ObDereferenceObject(dstFileObject);
}
if (NULL != dstFileHandle) {
ZwClose(dstFileHandle);
}
SipDirectPassThroughAndReturn(DeviceObject, Irp);
NoOverwrite:
//
// We got far enough to prepare the refcount change, but have decided that the
// file wasn't overwritten. Get out. Note that we need to complete the irp
// ourselves here, because we've already called into NTFS and stopped the
// irp completion processing.
//
SipCompleteCSRefcountChange(
perLink,
&perLink->Index,
perLink->CsFile,
FALSE,
FALSE);
SipDereferencePerLink(perLink);
status = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ASSERT(NULL == dstFileObject);
ASSERT(NULL == dstFileHandle);
return status;
}
NTSTATUS
SiDeleteCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PSIS_PER_LINK perLink = (PSIS_PER_LINK)Context;
KIRQL OldIrql;
PFILE_DISPOSITION_INFORMATION disposition;
UNREFERENCED_PARAMETER( DeviceObject );
if (Irp->PendingReturned) {
IoMarkIrpPending(Irp);
}
disposition = Irp->AssociatedIrp.SystemBuffer;
ASSERT(NULL != disposition);
// We just sent a delete setInformation call down to the link file. If it worked,
// we need to decrement the reference count on the underlying CS file (the file, not
// the SIS_CS_FILE object).
SIS_MARK_POINT_ULONG(perLink);
if (NT_SUCCESS(Irp->IoStatus.Status)) {
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
if (disposition->DeleteFile) {
perLink->Flags |= SIS_PER_LINK_DELETE_DISPOSITION_SET;
} else {
perLink->Flags &= ~SIS_PER_LINK_DELETE_DISPOSITION_SET;
}
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
}
SipEndDeleteModificationOperation(perLink,disposition->DeleteFile);
SIS_MARK_POINT_ULONG(perLink);
return STATUS_SUCCESS;
}
NTSTATUS
SiSetEofCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
A SetFileInformation with class FileEndOfFileInformation has completed.
We hold the scb for this file. Release it.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the set EOF request.
Context - Context parameter, a PSI_SET_EOF_COMPLETION_CONTEXT.
Return Value:
The function value for this routine is always success.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT fileObject;
PSIS_SCB scb = (PSIS_SCB)Context;
PFILE_END_OF_FILE_INFORMATION eofInfo;
LONGLONG newLength;
KIRQL OldIrql;
UNREFERENCED_PARAMETER( DeviceObject );
fileObject = irpSp->FileObject;
eofInfo = Irp->AssociatedIrp.SystemBuffer;
ASSERT(eofInfo);
newLength = eofInfo->EndOfFile.QuadPart;
SipAcquireScb(scb);
//
// If the set EOF succeeded, update our internal data structures to
// record the new file length.
//
if (NT_SUCCESS(Irp->IoStatus.Status)) {
ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
if (newLength != scb->SizeBackedByUnderlyingFile) {
//
// This call set the length to something other than the size of the
// underlying file, which means that the file is now dirty. Indicate so.
//
PSIS_PER_LINK perLink = scb->PerLink;
scb->Flags |= SIS_SCB_ANYTHING_IN_COPIED_FILE;
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
perLink->Flags |= SIS_PER_LINK_DIRTY;
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
}
if (newLength < scb->SizeBackedByUnderlyingFile) {
//
// This truncated the file.
//
SipTruncateScb(scb,newLength);
}
}
SipReleaseScb(scb);
//
// Propogate the IRP pending flag.
//
if (Irp->PendingReturned) {
IoMarkIrpPending( Irp );
}
return STATUS_SUCCESS;
}
NTSTATUS
SiSetInfo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is invoked on all NtSetInformationFile calls. We need to catch delete requests for files in
the common store, which need to be turned into delete requests for the links (and also result in
decrementing the ref count for the common store object, possibly deleting that as well.) All other
set information calls are handled by the normal parts of the driver stack.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the set file information request.
Context - Context parameter for this driver, unused.
Return Value:
The function value for this routine is always success.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
PDEVICE_EXTENSION deviceExtension;
PFILE_OBJECT fileObject = irpSp->FileObject;
FILE_INFORMATION_CLASS FileInformationClass;
NTSTATUS status;
PSIS_SCB scb;
PSIS_PER_FILE_OBJECT perFO;
PIO_STACK_LOCATION nextIrpSp;
SipHandleControlDeviceObject(DeviceObject, Irp);
FileInformationClass = irpSp->Parameters.SetFile.FileInformationClass;
if (FileRenameInformation == FileInformationClass) {
//
// We need to deal with the case where the target of a rename replace-if-exists
// is a SIS link. So, regardless of what type of file the source is, if this
// rename has replace-if-exists set, we have to deal with it.
//
PFILE_RENAME_INFORMATION fileRenameInfo = Irp->AssociatedIrp.SystemBuffer;
if (fileRenameInfo->ReplaceIfExists) {
//
// This is a replace-if-exists rename request. Let our special code handle it.
//
return SipReplaceIfExistsRename(DeviceObject, Irp);
}
}
if (!SipIsFileObjectSIS(fileObject,DeviceObject,FindActive,&perFO,&scb)) {
// This isn't a SIS file, just pass the call through to NTFS
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
ASSERT(scb);
SIS_MARK_POINT_ULONG(FileInformationClass);
SIS_MARK_POINT_ULONG(scb);
switch (FileInformationClass) {
case FileLinkInformation: {
//
// Don't allow hard links to SIS files.
//
status = STATUS_OBJECT_TYPE_MISMATCH;
SIS_MARK_POINT();
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
SIS_MARK_POINT();
return status;
}
case FileDispositionInformation: {
PFILE_DISPOSITION_INFORMATION disposition;
PSIS_PER_LINK perLink;
perLink = scb->PerLink;
disposition = Irp->AssociatedIrp.SystemBuffer;
SipBeginDeleteModificationOperation(perLink, disposition->DeleteFile);
//
// Send the delete irp down on the link/copied file.
//
nextIrpSp = IoGetNextIrpStackLocation( Irp );
RtlMoveMemory(nextIrpSp, irpSp, sizeof (IO_STACK_LOCATION));
IoSetCompletionRoutine( Irp,
SiDeleteCompletion,
perLink,
TRUE,
TRUE,
TRUE);
SIS_MARK_POINT_ULONG(scb);
status = IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
return status;
}
case FilePositionInformation: {
PFILE_POSITION_INFORMATION position = Irp->AssociatedIrp.SystemBuffer;
//
// Check if the file uses intermediate buffering. If it does
// then the new position we're supplied must be aligned properly
// for the device
//
if ((fileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
&& position->CurrentByteOffset.LowPart % deviceExtension->FilesystemVolumeSectorSize) {
status = STATUS_INVALID_PARAMETER;
} else {
//
// Just set the offset, regardless of whether it's beyond EOF.
//
fileObject->CurrentByteOffset = position->CurrentByteOffset;
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
SIS_MARK_POINT();
return status;
}
case FileEndOfFileInformation: {
PFILE_END_OF_FILE_INFORMATION endOfFile = Irp->AssociatedIrp.SystemBuffer;
SIS_MARK_POINT_ULONG(endOfFile->EndOfFile.LowPart);
#if DBG
if (BJBDebug & 0x10000) {
DbgPrint("SIS: SiSetInfo: set EOF information scb %p, AO %d, eof.lp 0x%x\n",
scb,irpSp->Parameters.SetFile.AdvanceOnly,endOfFile->EndOfFile.LowPart);
}
#endif // DBG
if (irpSp->Parameters.SetFile.AdvanceOnly) {
//
// This is the lazywriter advancing valid data length, so it's guaranteed
// not to truncate the file, so we don't need to pay attention to it.
// Just pass it down to NTFS unattended.
//
SIS_MARK_POINT_ULONG(scb);
SipDirectPassThroughAndReturn(DeviceObject,Irp);
}
nextIrpSp = IoGetNextIrpStackLocation(Irp);
RtlMoveMemory(nextIrpSp, irpSp, sizeof(IO_STACK_LOCATION));
//
// We've completed the copy on write. Pass the call down on the copied file object.
// Hold the Scb while the call goes down, and release it in the completion routine.
//
IoSetCompletionRoutine(
Irp,
SiSetEofCompletion,
scb,
TRUE,
TRUE,
TRUE);
return IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
}
default: {
// Just pass it down
SIS_MARK_POINT();
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
}
/*NOTREACHED*/
ASSERT(FALSE && "NOTREACHED");
SIS_MARK_POINT();
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
NTSTATUS
SiQueryBasicInfoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
A QueryInformationFile with class FileBasicInformation has completed successfully
for a non-FILE_OPEN_REPARSE_POINT file. Clear out the reparse and sparse flags.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the query basic information
request.
Context - Context parameter, a PSIS_PER_FILE_OBJECT.
Return Value:
STATUS_SUCCESS.
--*/
{
PFILE_BASIC_INFORMATION basicInfo = (PFILE_BASIC_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(DeviceObject);
ASSERT(NULL != basicInfo);
SIS_MARK_POINT_ULONG(Irp);
basicInfo->FileAttributes &= ~(FILE_ATTRIBUTE_SPARSE_FILE|FILE_ATTRIBUTE_REPARSE_POINT);
//
// If there are no remaining attributes set, explicitly set FILE_ATTRIBUTE_NORMAL.
//
if (0 == basicInfo->FileAttributes) {
basicInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
}
return STATUS_SUCCESS;
}
NTSTATUS
SiQueryAllInfoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
A QueryInformationFile with class FileAllInformation has completed successfully
for a non-FILE_OPEN_REPARSE_POINT file. Clear out the reparse and sparse flags.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the query all information
request
Context - Context parameter, a PSIS_PER_FILE_OBJECT.
Return Value:
STATUS_SUCCESS.
--*/
{
PFILE_ALL_INFORMATION allInfo = (PFILE_ALL_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(DeviceObject);
ASSERT(NULL != allInfo);
SIS_MARK_POINT_ULONG(Irp);
allInfo->BasicInformation.FileAttributes &= ~(FILE_ATTRIBUTE_SPARSE_FILE|FILE_ATTRIBUTE_REPARSE_POINT);
//
// If there are no remaining attributes set, explicitly set FILE_ATTRIBUTE_NORMAL.
//
if (0 == allInfo->BasicInformation.FileAttributes) {
allInfo->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
}
return STATUS_SUCCESS;
}
NTSTATUS
SiQueryNetworkOpenInfoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
A QueryInformationFile with class FileNetworkOpenInformation has completed successfully
for a non-FILE_OPEN_REPARSE_POINT file. Clear out the reparse and sparse flags.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the query network open information
request
Context - Context parameter, a PSIS_PER_FILE_OBJECT.
Return Value:
STATUS_SUCCESS.
--*/
{
PFILE_NETWORK_OPEN_INFORMATION netOpenInfo = (PFILE_NETWORK_OPEN_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(DeviceObject);
ASSERT(NULL != netOpenInfo);
SIS_MARK_POINT_ULONG(Irp);
netOpenInfo->FileAttributes &= ~(FILE_ATTRIBUTE_SPARSE_FILE|FILE_ATTRIBUTE_REPARSE_POINT);
//
// If there are no remaining attributes set, explicitly set FILE_ATTRIBUTE_NORMAL.
//
if (0 == netOpenInfo->FileAttributes) {
netOpenInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
}
return STATUS_SUCCESS;
}
NTSTATUS
SiQueryAttributeTagInfoCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
/*++
Routine Description:
A QueryInformationFile with class FileAttributeTagInformation has completed successfully
for a non-FILE_OPEN_REPARSE_POINT file. Clear out the reparse and sparse flags, and set
the reparse tag as if it were not a reparse point.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the query attribute tag request.
Context - Context parameter, a PSIS_PER_FILE_OBJECT.
Return Value:
STATUS_SUCCESS.
--*/
{
PFILE_ATTRIBUTE_TAG_INFORMATION attributeTagInfo = (PFILE_ATTRIBUTE_TAG_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(DeviceObject);
ASSERT(NULL != attributeTagInfo);
SIS_MARK_POINT_ULONG(Irp);
attributeTagInfo->FileAttributes &= ~(FILE_ATTRIBUTE_SPARSE_FILE|FILE_ATTRIBUTE_REPARSE_POINT);
attributeTagInfo->ReparseTag = IO_REPARSE_TAG_RESERVED_ZERO;
//
// If there are no remaining attributes set, explicitly set FILE_ATTRIBUTE_NORMAL.
//
if (0 == attributeTagInfo->FileAttributes) {
attributeTagInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
}
return STATUS_SUCCESS;
}
NTSTATUS
SiQueryInfo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
The top level entry point for a query information irp. We do special
processing for a few of these, check to see if it's one and dispatch
appropriately.
Arguments:
DeviceObject - Pointer to this driver's device object.
Irp - Pointer to the I/O Request Packet representing the query request.
Return Value:
status of the request, typically returned from NTFS
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
PIO_STACK_LOCATION nextIrpSp;
PFILE_OBJECT fileObject = irpSp->FileObject;
PSIS_PER_FILE_OBJECT perFO;
PIO_COMPLETION_ROUTINE completionRoutine;
KIRQL OldIrql;
BOOLEAN openedAsReparsePoint;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
SipHandleControlDeviceObject(DeviceObject, Irp);
if (!SipIsFileObjectSIS(fileObject,DeviceObject,FindActive,&perFO,NULL)) {
// This isn't a SIS file, just pass the call through to NTFS
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
KeAcquireSpinLock(perFO->SpinLock, &OldIrql);
openedAsReparsePoint = (perFO->Flags & SIS_PER_FO_OPEN_REPARSE) ? TRUE : FALSE;
KeReleaseSpinLock(perFO->SpinLock, OldIrql);
if (openedAsReparsePoint) {
//
// This was opened as a reparse point, so let the user see the real truth.
//
SipDirectPassThroughAndReturn(DeviceObject, Irp);
}
SIS_MARK_POINT_ULONG(irpSp->Parameters.QueryFile.FileInformationClass);
SIS_MARK_POINT_ULONG(perFO->fc->primaryScb);
#if DBG
if (BJBDebug & 0x10) {
DbgPrint("SIS: SiQueryInfo: InformationClass %d\n",
irpSp->Parameters.QueryFile.FileInformationClass);
}
#endif // DBG
// Handle the request.
//
switch (irpSp->Parameters.QueryFile.FileInformationClass) {
case FileDirectoryInformation: // 1 // diretory-only call, let NTFS reject it
case FileFullDirectoryInformation: // 2 // diretory-only call, let NTFS reject it
case FileBothDirectoryInformation: // 3 // diretory-only call, let NTFS reject it
// 4 // FileBasicInfo handled separately
case FileStandardInformation: // 5
case FileInternalInformation: // 6
case FileEaInformation: // 7
case FileAccessInformation: // 8
case FileNameInformation: // 9
case FileRenameInformation: // 10 // rename isn't valid for query, but we'll let NTFS reject it
case FileLinkInformation: // 11 // link isn't valid for query, but we'll let NTFS reject it
case FileNamesInformation: // 12 // diretory-only call, let NTFS reject it
case FileDispositionInformation: // 13
case FilePositionInformation: // 14
case FileFullEaInformation: // 15 // NTFS doesn't support this, but we'll let it reject it
case FileModeInformation: // 16
case FileAlignmentInformation: // 17
// 18 // FileAllInformation handled separately
case FileAllocationInformation: // 19
case FileEndOfFileInformation: // 20
case FileAlternateNameInformation: // 21
case FileStreamInformation: // 22
case FilePipeInformation: // 23 // NTFS doesn't support this, but we'll let it reject it
case FilePipeLocalInformation: // 24 // NTFS doesn't support this, but we'll let it reject it
case FilePipeRemoteInformation: // 25 // NTFS doesn't support this, but we'll let it reject it
case FileMailslotQueryInformation: // 26 // NTFS doesn't support this, but we'll let it reject it
case FileMailslotSetInformation: // 27 // NTFS doesn't support this, but we'll let it reject it
case FileCompressionInformation: // 28
case FileObjectIdInformation: // 29
case FileCompletionInformation: // 30 // NTFS doesn't support this, but we'll let it reject it
// 31 FileMoveCluserInformation - intentionally failed for SIS files (+ not supported by NTFS)
case FileQuotaInformation: // 32 // diretory-only call, let NTFS reject it
case FileReparsePointInformation: // 33 // diretory-only call, let NTFS reject it
// 34 // FileNetworkOpenInformation handled separately
// 35 // FileAttributeTagInformation handled separately
case FileTrackingInformation: // 36 // NTFS doesn't support this, but we'll let it reject it
//
// Set the completion routine to NULL, which says that we never want to
// catch completions on these calls.
//
completionRoutine = NULL;
break;
case FileBasicInformation: // 4
completionRoutine = SiQueryBasicInfoCompletion;
break;
case FileAllInformation: // 18
completionRoutine = SiQueryAllInfoCompletion;
break;
case FileNetworkOpenInformation: // 34
completionRoutine = SiQueryNetworkOpenInfoCompletion;
break;
case FileAttributeTagInformation: // 35
completionRoutine = SiQueryAttributeTagInfoCompletion;
break;
case FileMoveClusterInformation: // 31 // NTFS doesn't implement this, but it's too scary to pass through anyway
//
// Reject these calls, for now.
//
SIS_MARK_POINT_ULONG(perFO->fc->primaryScb);
#if DBG
DbgPrint("SIS: SiQueryInfo: aborting FileInformationClass %d\n",irpSp->Parameters.QueryFile.FileInformationClass);
#endif // DBG
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
default:
//
// It's an unknown or invalid information class. Assume that it's newly added and
// benign, and pass it down to NTFS unmodified.
//
SIS_MARK_POINT_ULONG(perFO->fc->primaryScb);
SIS_MARK_POINT_ULONG(irpSp->Parameters.QueryFile.FileInformationClass);
completionRoutine = NULL;
#if DBG
DbgPrint("SIS: SiQueryInfo: passing though unknown FileInformationClass %d\n",irpSp->Parameters.QueryFile.FileInformationClass);
#endif // DBG
break;
}
if (NULL == completionRoutine) {
//
// This call doesn't require fixup on completion. Pass it through.
//
SipDirectPassThroughAndReturn(DeviceObject, Irp);
} else {
//
// This call requires fixup on the way out. Only invoke the
// completion routine on success; else, there's nothing to fix up.
//
nextIrpSp = IoGetNextIrpStackLocation( Irp );
RtlMoveMemory(nextIrpSp, irpSp, sizeof (IO_STACK_LOCATION));
IoSetCompletionRoutine(
Irp,
completionRoutine,
perFO,
TRUE,
FALSE,
FALSE);
return IoCallDriver(deviceExtension->AttachedToDeviceObject, Irp);
}
}
VOID
SipTruncateScb(
IN OUT PSIS_SCB scb,
IN LONGLONG newLength)
{
LONGLONG newLengthInSectors;
PDEVICE_EXTENSION deviceExtension;
deviceExtension = (PDEVICE_EXTENSION)scb->PerLink->CsFile->DeviceObject->DeviceExtension;
newLengthInSectors = (newLength + deviceExtension->FilesystemVolumeSectorSize - 1) /
deviceExtension->FilesystemVolumeSectorSize;
ASSERT(newLength < scb->SizeBackedByUnderlyingFile); // else this isn't a truncation
FsRtlTruncateLargeMcb(scb->Ranges,newLengthInSectors);
scb->SizeBackedByUnderlyingFile = newLength;
}
VOID
SipBeginDeleteModificationOperation(
IN OUT PSIS_PER_LINK perLink,
IN BOOLEAN delete)
/*++
Routine Description:
We are beginning a delete or undelete operation on the given
per-link. If there is the opposite kind of operation in
progress, wait for it to complete. Otherwise, indicate that
the new operation is in progress and continue.
Must be called at IRQL < DISPATCH_LEVEL.
Arguments:
perLink - the per link for the file on which we're doing the (un)delete.
delete - TRUE for delete, FALSE for undelete
Return Value:
void
--*/
{
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
while ((perLink->PendingDeleteCount > 0) &&
((delete && (perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS)) ||
(((!delete) && !(perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS))))) {
//
// The wrong kind of operation is happening now, so we need to block.
//
if (!(perLink->Flags & SIS_PER_LINK_DELETE_WAITERS)) {
KeClearEvent(perLink->DeleteEvent);
perLink->Flags |= SIS_PER_LINK_DELETE_WAITERS;
}
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
KeWaitForSingleObject(perLink->DeleteEvent, Executive, KernelMode, FALSE, NULL);
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
}
if (!delete) {
ASSERT((perLink->PendingDeleteCount == 0) || (perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS));
perLink->Flags |= SIS_PER_LINK_UNDELETE_IN_PROGRESS;
} else {
ASSERT(!(perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS));
}
perLink->PendingDeleteCount++;
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
}
VOID
SipEndDeleteModificationOperation(
IN OUT PSIS_PER_LINK perLink,
IN BOOLEAN delete)
/*++
Routine Description:
We just finished a delete/undelete operation. Decrement our count and
if appropriate wake up any waiters.
Must be called with IRQL <= DISPATCH_LEVEL.
Arguments:
perLink - the per link for the file on which we're doing the (un)delete.
delete - TRUE for delete, FALSE for undelete
Return Value:
void
--*/
{
KIRQL OldIrql;
KeAcquireSpinLock(perLink->SpinLock, &OldIrql);
ASSERT(perLink->PendingDeleteCount > 0);
perLink->PendingDeleteCount--;
#if DBG
if (delete) {
ASSERT(!(perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS));
} else {
ASSERT(perLink->Flags & SIS_PER_LINK_UNDELETE_IN_PROGRESS);
}
#endif // DBG
if (0 == perLink->PendingDeleteCount) {
if (perLink->Flags & SIS_PER_LINK_DELETE_WAITERS) {
perLink->Flags &= ~SIS_PER_LINK_DELETE_WAITERS;
KeSetEvent(perLink->DeleteEvent, IO_NO_INCREMENT, FALSE);
}
if (!delete) {
perLink->Flags &= ~SIS_PER_LINK_UNDELETE_IN_PROGRESS;
}
}
KeReleaseSpinLock(perLink->SpinLock, OldIrql);
}