mirror of https://github.com/tongzx/nt5src
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.
992 lines
34 KiB
992 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FsCtrl.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File System Control routines for Rdbss.
|
|
Fsctls on the device fcb are handled in another module.
|
|
|
|
Author:
|
|
|
|
Joe Linn [JoeLinn] 7-mar-95
|
|
|
|
Revision History:
|
|
|
|
Balan Sethu Raman 18-May-95 -- Integrated with mini rdrs
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include <dfsfsctl.h>
|
|
#include "fsctlbuf.h"
|
|
|
|
// The local debug trace level
|
|
|
|
#define Dbg (DEBUG_TRACE_FSCTRL)
|
|
|
|
// Local procedure prototypes
|
|
|
|
NTSTATUS
|
|
RxUserFsCtrl ( RXCOMMON_SIGNATURE );
|
|
|
|
NTSTATUS
|
|
TranslateSisFsctlName (
|
|
IN PWCHAR InputName,
|
|
OUT PUNICODE_STRING RelativeName,
|
|
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
|
|
IN PUNICODE_STRING NetRootName
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxCommonFileSystemControl)
|
|
#pragma alloc_text(PAGE, RxUserFsCtrl)
|
|
#pragma alloc_text(PAGE, RxLowIoFsCtlShell)
|
|
#pragma alloc_text(PAGE, RxLowIoFsCtlShellCompletion)
|
|
#pragma alloc_text(PAGE, TranslateSisFsctlName)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RxCommonFileSystemControl ( RXCOMMON_SIGNATURE )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common routine for doing FileSystem control operations called
|
|
by both the fsd and fsp threads. What happens is that we pick off fsctls that
|
|
we know about and remote the rest....remoting means sending them thru the
|
|
lowio stuff which may/will pick off a few more. the ones that we pick off here
|
|
(and currently return STATUS_NOT_IMPLEMENTED) and the ones for being an oplock
|
|
provider and for doing volume mounts....we don't even have volume fcbs
|
|
yet since this is primarily a localFS concept.
|
|
nevertheless, these are not passed thru to the mini.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
RxCaptureFileObject;
|
|
|
|
NTSTATUS Status;
|
|
NODE_TYPE_CODE TypeOfOpen;
|
|
BOOLEAN TryLowIo = TRUE;
|
|
ULONG FsControlCode = capPARAMS->Parameters.FileSystemControl.FsControlCode;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCommonFileSystemControl %08lx\n", RxContext));
|
|
RxDbgTrace( 0, Dbg, ("Irp = %08lx\n", capReqPacket));
|
|
RxDbgTrace( 0, Dbg, ("MinorFunction = %08lx\n", capPARAMS->MinorFunction));
|
|
RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));
|
|
|
|
RxLog(("FsCtl %x %x %x %x",RxContext,capReqPacket,capPARAMS->MinorFunction,FsControlCode));
|
|
RxWmiLog(LOG,
|
|
RxCommonFileSystemControl,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capReqPacket)
|
|
LOGUCHAR(capPARAMS->MinorFunction)
|
|
LOGULONG(FsControlCode));
|
|
|
|
ASSERT(capPARAMS->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);
|
|
|
|
// Validate the buffers passed in for the FSCTL
|
|
if ((capReqPacket->RequestorMode == UserMode) &&
|
|
(!FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_IN_FSP))) {
|
|
try {
|
|
switch (FsControlCode & 3) {
|
|
case METHOD_NEITHER:
|
|
{
|
|
PVOID pInputBuffer,pOutputBuffer;
|
|
ULONG InputBufferLength,OutputBufferLength;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
pInputBuffer = METHODNEITHER_OriginalInputBuffer(capPARAMS);
|
|
pOutputBuffer = METHODNEITHER_OriginalOutputBuffer(capReqPacket);
|
|
|
|
InputBufferLength = capPARAMS->Parameters.FileSystemControl.InputBufferLength;
|
|
OutputBufferLength = capPARAMS->Parameters.FileSystemControl.OutputBufferLength;
|
|
|
|
if (pInputBuffer != NULL) {
|
|
ProbeForRead(
|
|
pInputBuffer,
|
|
InputBufferLength,
|
|
1);
|
|
|
|
ProbeForWrite(
|
|
pInputBuffer,
|
|
InputBufferLength,
|
|
1);
|
|
} else if (InputBufferLength != 0) {
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (pOutputBuffer != NULL) {
|
|
ProbeForRead(
|
|
pOutputBuffer,
|
|
OutputBufferLength,
|
|
1);
|
|
|
|
ProbeForWrite(
|
|
pOutputBuffer,
|
|
OutputBufferLength,
|
|
1);
|
|
} else if (OutputBufferLength != 0) {
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case METHOD_BUFFERED:
|
|
case METHOD_IN_DIRECT:
|
|
case METHOD_OUT_DIRECT:
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Status = STATUS_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
switch (capPARAMS->MinorFunction) {
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
case IRP_MN_TRACK_LINK:
|
|
{
|
|
|
|
RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));
|
|
switch (FsControlCode) {
|
|
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:
|
|
case FSCTL_OPLOCK_BREAK_ACK_NO_2:
|
|
{
|
|
// fsrtl oplock package is handled in common for all minirdrs
|
|
|
|
//Status = RxOplockRequest( RXCOMMON_ARGUMENTS, &PostToFsp );
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
TryLowIo = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FSCTL_LOCK_VOLUME:
|
|
case FSCTL_UNLOCK_VOLUME:
|
|
case FSCTL_DISMOUNT_VOLUME:
|
|
case FSCTL_MARK_VOLUME_DIRTY:
|
|
case FSCTL_IS_VOLUME_MOUNTED:
|
|
{
|
|
// Decode the file object, the only type of opens we accept are
|
|
// user volume opens.
|
|
TypeOfOpen = NodeType(capFcb);
|
|
|
|
if (TypeOfOpen != RDBSS_NTC_VOLUME_FCB) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
TryLowIo = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FSCTL_DFS_GET_REFERRALS:
|
|
case FSCTL_DFS_REPORT_INCONSISTENCY:
|
|
{
|
|
if (!BooleanFlagOn(capFcb->pNetRoot->pSrvCall->Flags,SRVCALL_FLAG_DFS_AWARE_SERVER)) {
|
|
TryLowIo = FALSE;
|
|
Status = STATUS_DFS_UNAVAILABLE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FSCTL_LMR_GET_LINK_TRACKING_INFORMATION:
|
|
{
|
|
// Validate the parameters and reject illformed requests
|
|
|
|
ULONG OutputBufferLength;
|
|
PLINK_TRACKING_INFORMATION pLinkTrackingInformation;
|
|
|
|
OutputBufferLength = capPARAMS->Parameters.FileSystemControl.OutputBufferLength;
|
|
pLinkTrackingInformation = capReqPacket->AssociatedIrp.SystemBuffer;
|
|
|
|
TryLowIo = FALSE;
|
|
|
|
if ((OutputBufferLength < sizeof(LINK_TRACKING_INFORMATION)) ||
|
|
(pLinkTrackingInformation == NULL) ||
|
|
(capFcb->pNetRoot->Type != NET_ROOT_DISK)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
BYTE Buffer[
|
|
sizeof(FILE_FS_OBJECTID_INFORMATION)];
|
|
|
|
PFILE_FS_OBJECTID_INFORMATION pVolumeInformation;
|
|
|
|
pVolumeInformation = (PFILE_FS_OBJECTID_INFORMATION)Buffer;
|
|
RxContext->Info.FsInformationClass = FileFsObjectIdInformation;
|
|
RxContext->Info.Buffer = pVolumeInformation;
|
|
RxContext->Info.LengthRemaining = sizeof(Buffer);
|
|
|
|
MINIRDR_CALL(
|
|
Status,
|
|
RxContext,
|
|
capFcb->MRxDispatch,
|
|
MRxQueryVolumeInfo,
|
|
(RxContext));
|
|
|
|
if ((Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_BUFFER_OVERFLOW)) {
|
|
// Copy the volume Id onto the net root.
|
|
RtlCopyMemory(
|
|
&capFcb->pNetRoot->DiskParameters.VolumeId,
|
|
pVolumeInformation->ObjectId,
|
|
sizeof(GUID));
|
|
|
|
RtlCopyMemory(
|
|
pLinkTrackingInformation->VolumeId,
|
|
&capFcb->pNetRoot->DiskParameters.VolumeId,
|
|
sizeof(GUID));
|
|
|
|
if (FlagOn(capFcb->pNetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT)) {
|
|
pLinkTrackingInformation->Type = DfsLinkTrackingInformation;
|
|
} else {
|
|
pLinkTrackingInformation->Type = NtfsLinkTrackingInformation;
|
|
}
|
|
|
|
capReqPacket->IoStatus.Information = sizeof(LINK_TRACKING_INFORMATION);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
capReqPacket->IoStatus.Status = Status;
|
|
}
|
|
break;
|
|
|
|
case FSCTL_SET_ZERO_DATA:
|
|
{
|
|
PFILE_ZERO_DATA_INFORMATION ZeroRange;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Verify if the request is well formed...
|
|
// a. check if the input buffer length is OK
|
|
if (capPARAMS->Parameters.FileSystemControl.InputBufferLength <
|
|
sizeof( FILE_ZERO_DATA_INFORMATION )) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
//
|
|
// b: ensure the ZeroRange request is properly formed.
|
|
//
|
|
|
|
ZeroRange = (PFILE_ZERO_DATA_INFORMATION)capReqPacket->AssociatedIrp.SystemBuffer;
|
|
|
|
if ((ZeroRange->FileOffset.QuadPart < 0) ||
|
|
(ZeroRange->BeyondFinalZero.QuadPart < 0) ||
|
|
(ZeroRange->FileOffset.QuadPart > ZeroRange->BeyondFinalZero.QuadPart)) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// Before the request can be processed ensure that there
|
|
// are no user mapped sections
|
|
|
|
if (!MmCanFileBeTruncated(
|
|
&capFcb->NonPaged->SectionObjectPointers,
|
|
NULL)) {
|
|
|
|
Status = STATUS_USER_MAPPED_FILE;
|
|
}
|
|
}
|
|
|
|
TryLowIo = (Status == STATUS_SUCCESS);
|
|
}
|
|
break;
|
|
|
|
case FSCTL_SET_COMPRESSION:
|
|
case FSCTL_SET_SPARSE:
|
|
{
|
|
// Ensure that the close is not delayed is for these FCB's
|
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|
|
|
if ((Status == STATUS_LOCK_NOT_GRANTED) &&
|
|
(!BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT))) {
|
|
|
|
RxDbgTrace(0, Dbg, ("Cannot acquire Fcb\n", 0));
|
|
|
|
RxContext->PostRequest = TRUE;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
TryLowIo = FALSE;
|
|
} else {
|
|
ClearFlag(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
|
|
if (FsControlCode == FSCTL_SET_SPARSE) {
|
|
if (NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE) {
|
|
capFcb->Attributes |= FILE_ATTRIBUTE_SPARSE_FILE;
|
|
|
|
capFobx->pSrvOpen->BufferingFlags = 0;
|
|
|
|
RxChangeBufferingState(
|
|
(PSRV_OPEN)capFobx->pSrvOpen,
|
|
NULL,
|
|
FALSE);
|
|
} else {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
RxReleaseFcb(RxContext,capFcb);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FSCTL_SIS_COPYFILE:
|
|
{
|
|
//
|
|
// This is the single-instance store copy FSCTL. The input
|
|
// paths are fully qualified NT paths and must be made
|
|
// relative to the share (which must be the same for both
|
|
// names).
|
|
//
|
|
|
|
PSI_COPYFILE copyFile = capReqPacket->AssociatedIrp.SystemBuffer;
|
|
ULONG bufferLength = capPARAMS->Parameters.FileSystemControl.InputBufferLength;
|
|
PWCHAR source;
|
|
PWCHAR dest;
|
|
UNICODE_STRING sourceString;
|
|
UNICODE_STRING destString;
|
|
|
|
memset(&sourceString, 0, sizeof(sourceString));
|
|
memset(&destString, 0, sizeof(destString));
|
|
|
|
// Validate the buffer passed in
|
|
if ((copyFile == NULL) ||
|
|
(bufferLength < sizeof(SI_COPYFILE))) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
TryLowIo = FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Get pointers to the two names.
|
|
//
|
|
|
|
source = copyFile->FileNameBuffer;
|
|
dest = source + (copyFile->SourceFileNameLength / sizeof(WCHAR));
|
|
|
|
//
|
|
// Verify that the inputs are reasonable.
|
|
//
|
|
|
|
if ( (copyFile->SourceFileNameLength > bufferLength) ||
|
|
(copyFile->DestinationFileNameLength > bufferLength) ||
|
|
(copyFile->SourceFileNameLength < sizeof(WCHAR)) ||
|
|
(copyFile->DestinationFileNameLength < sizeof(WCHAR)) ||
|
|
((FIELD_OFFSET(SI_COPYFILE,FileNameBuffer) +
|
|
copyFile->SourceFileNameLength +
|
|
copyFile->DestinationFileNameLength) > bufferLength) ||
|
|
(*(source + (copyFile->SourceFileNameLength/sizeof(WCHAR)-1)) != 0) ||
|
|
(*(dest + (copyFile->DestinationFileNameLength/sizeof(WCHAR)-1)) != 0) ) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
TryLowIo = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Perform symbolic link translation on the source and destination names,
|
|
// and ensure that they translate to redirector names.
|
|
//
|
|
|
|
Status = TranslateSisFsctlName(
|
|
source,
|
|
&sourceString,
|
|
capFcb->RxDeviceObject,
|
|
&((PNET_ROOT)capFcb->pNetRoot)->PrefixEntry.Prefix );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
TryLowIo = FALSE;
|
|
break;
|
|
}
|
|
|
|
Status = TranslateSisFsctlName(
|
|
dest,
|
|
&destString,
|
|
capFcb->RxDeviceObject,
|
|
&((PNET_ROOT)capFcb->pNetRoot)->PrefixEntry.Prefix );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
RtlFreeUnicodeString( &sourceString );
|
|
TryLowIo = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Convert the paths in the input buffer into share-relative
|
|
// paths.
|
|
//
|
|
|
|
if ( (ULONG)(sourceString.MaximumLength + destString.MaximumLength) >
|
|
(copyFile->SourceFileNameLength + copyFile->DestinationFileNameLength) ) {
|
|
PSI_COPYFILE newCopyFile;
|
|
ULONG length = FIELD_OFFSET(SI_COPYFILE,FileNameBuffer) +
|
|
sourceString.MaximumLength + destString.MaximumLength;
|
|
ASSERT( length > capPARAMS->Parameters.FileSystemControl.InputBufferLength );
|
|
newCopyFile = RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
length,
|
|
RX_MISC_POOLTAG);
|
|
if (newCopyFile == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TryLowIo = FALSE;
|
|
break;
|
|
}
|
|
newCopyFile->Flags = copyFile->Flags;
|
|
ExFreePool( copyFile );
|
|
copyFile = newCopyFile;
|
|
capReqPacket->AssociatedIrp.SystemBuffer = copyFile;
|
|
capPARAMS->Parameters.FileSystemControl.InputBufferLength = length;
|
|
}
|
|
|
|
copyFile->SourceFileNameLength = sourceString.MaximumLength;
|
|
copyFile->DestinationFileNameLength = destString.MaximumLength;
|
|
source = copyFile->FileNameBuffer;
|
|
dest = source + (copyFile->SourceFileNameLength / sizeof(WCHAR));
|
|
RtlCopyMemory( source, sourceString.Buffer, copyFile->SourceFileNameLength );
|
|
RtlCopyMemory( dest, destString.Buffer, copyFile->DestinationFileNameLength );
|
|
|
|
RtlFreeUnicodeString( &sourceString );
|
|
RtlFreeUnicodeString( &destString );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (TryLowIo) {
|
|
Status = RxLowIoFsCtlShell(RxContext);
|
|
}
|
|
|
|
if (RxContext->PostRequest) {
|
|
Status = RxFsdPostRequest(RxContext);
|
|
} else {
|
|
if (Status == STATUS_PENDING) {
|
|
RxDereferenceAndDeleteRxContext(RxContext);
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCommonFileSystemControl -> %08lx\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG RxEnablePeekBackoff = 1;
|
|
|
|
NTSTATUS
|
|
RxLowIoFsCtlShell( RXCOMMON_SIGNATURE )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common routine for implementing the user's requests made
|
|
through NtFsControlFile.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFcb;
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFobx;
|
|
RxCaptureFileObject;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN PostToFsp = FALSE;
|
|
|
|
NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = capPARAMS->Parameters.FileSystemControl.FsControlCode;
|
|
BOOLEAN SubmitLowIoRequest = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxLowIoFsCtlShell...\n", 0));
|
|
RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));
|
|
|
|
RxInitializeLowIoContext(pLowIoContext,LOWIO_OP_FSCTL);
|
|
|
|
switch (capPARAMS->MinorFunction) {
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
{
|
|
// The RDBSS filters out those FsCtls that can be handled without the intervention
|
|
// of the mini rdr's. Currently all FsCtls are forwarded down to the mini rdr.
|
|
switch (FsControlCode) {
|
|
case FSCTL_PIPE_PEEK:
|
|
{
|
|
if ((capReqPacket->AssociatedIrp.SystemBuffer != NULL) &&
|
|
(capPARAMS->Parameters.FileSystemControl.OutputBufferLength >=
|
|
(ULONG)FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]))) {
|
|
|
|
PFILE_PIPE_PEEK_BUFFER pPeekBuffer;
|
|
|
|
pPeekBuffer = (PFILE_PIPE_PEEK_BUFFER)capReqPacket->AssociatedIrp.SystemBuffer;
|
|
|
|
RtlZeroMemory(
|
|
pPeekBuffer,
|
|
capPARAMS->Parameters.FileSystemControl.OutputBufferLength);
|
|
|
|
if (RxShouldRequestBeThrottled(&capFobx->Specific.NamedPipe.ThrottlingState) &&
|
|
RxEnablePeekBackoff) {
|
|
|
|
SubmitLowIoRequest = FALSE;
|
|
|
|
RxDbgTrace(
|
|
0, (DEBUG_TRACE_ALWAYS),
|
|
("RxLowIoFsCtlShell: Throttling Peek Request\n"));
|
|
|
|
capReqPacket->IoStatus.Information = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER,Data);
|
|
pPeekBuffer->ReadDataAvailable = 0;
|
|
pPeekBuffer->NamedPipeState = FILE_PIPE_CONNECTED_STATE;
|
|
pPeekBuffer->NumberOfMessages = MAXULONG;
|
|
pPeekBuffer->MessageLength = 0;
|
|
|
|
RxContext->StoredStatus = STATUS_SUCCESS;
|
|
|
|
Status = RxContext->StoredStatus;
|
|
} else {
|
|
RxDbgTrace(
|
|
0, (DEBUG_TRACE_ALWAYS),
|
|
("RxLowIoFsCtlShell: Throttling queries %ld\n",
|
|
capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
|
|
RxLog(
|
|
("ThrottlQs %lx %lx %lx %ld\n",
|
|
RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
|
|
capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
RxWmiLog(LOG,
|
|
RxLowIoFsCtlShell,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFobx)
|
|
LOGULONG(capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
}
|
|
} else {
|
|
RxContext->StoredStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (SubmitLowIoRequest) {
|
|
Status = RxLowIoSubmit(RxContext,RxLowIoFsCtlShellCompletion);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxLowIoFsCtlShell -> %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxLowIoFsCtlShellCompletion( RXCOMMON_SIGNATURE )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for FSCTL requests passed down to the mini rdr
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp being processed
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
|
|
NTSTATUS Status;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = RxContext->StoredStatus;
|
|
RxDbgTrace(+1, Dbg, ("RxLowIoFsCtlShellCompletion entry Status = %08lx\n", Status));
|
|
|
|
switch (FsControlCode) {
|
|
case FSCTL_PIPE_PEEK:
|
|
{
|
|
if ((Status == STATUS_SUCCESS) ||
|
|
(Status == STATUS_BUFFER_OVERFLOW)) {
|
|
// In the case of Peek operations a throttle mechanism is in place to
|
|
// prevent the network from being flodded with requests which return 0
|
|
// bytes.
|
|
|
|
PFILE_PIPE_PEEK_BUFFER pPeekBuffer;
|
|
|
|
pPeekBuffer = (PFILE_PIPE_PEEK_BUFFER)pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
|
|
if (pPeekBuffer->ReadDataAvailable == 0) {
|
|
|
|
// The peek request returned zero bytes.
|
|
|
|
RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShellCompletion: Enabling Throttling for Peek Request\n"));
|
|
RxInitiateOrContinueThrottling(&capFobx->Specific.NamedPipe.ThrottlingState);
|
|
RxLog(("ThrottlYes %lx %lx %lx %ld\n",
|
|
RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
|
|
capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
RxWmiLog(LOG,
|
|
RxLowIoFsCtlShellCompletion_1,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFobx)
|
|
LOGULONG(capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
} else {
|
|
|
|
RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShellCompletion: Disabling Throttling for Peek Request\n"));
|
|
RxTerminateThrottling(&capFobx->Specific.NamedPipe.ThrottlingState);
|
|
RxLog(("ThrottlNo %lx %lx %lx %ld\n",
|
|
RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
|
|
capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
RxWmiLog(LOG,
|
|
RxLowIoFsCtlShellCompletion_2,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFobx)
|
|
LOGULONG(capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
}
|
|
|
|
capReqPacket->IoStatus.Information = RxContext->InformationToReturn;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if ((Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_SUCCESS)) {
|
|
capReqPacket->IoStatus.Information = RxContext->InformationToReturn;
|
|
}
|
|
break;
|
|
}
|
|
|
|
capReqPacket->IoStatus.Status = Status;
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxLowIoFsCtlShellCompletion exit Status = %08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
TranslateSisFsctlName(
|
|
IN PWCHAR InputName,
|
|
OUT PUNICODE_STRING RelativeName,
|
|
IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
|
|
IN PUNICODE_STRING NetRootName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a fully qualified name into a share-relative name.
|
|
It is used to munge the input buffer of the SIS_COPYFILE FSCTL, which
|
|
takes two fully qualified NT paths as inputs.
|
|
|
|
The routine operates by translating the input path as necessary to get
|
|
to an actual device name, verifying that the target device is the
|
|
redirector, and verifying that the target server/share is the one on
|
|
which the I/O was issued.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING currentString;
|
|
UNICODE_STRING testString;
|
|
PWCHAR p;
|
|
PWCHAR q;
|
|
HANDLE handle;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
PWCHAR translationBuffer = NULL;
|
|
ULONG translationLength;
|
|
ULONG remainingLength;
|
|
ULONG resultLength;
|
|
|
|
RtlInitUnicodeString( ¤tString, InputName );
|
|
|
|
p = currentString.Buffer;
|
|
|
|
if (!p)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( (*p == L'\\') && (*(p+1) == L'\\') ) {
|
|
|
|
//
|
|
// Special case for name that starts with \\ (i.e., a UNC name):
|
|
// assume that the \\ would translate to the redirector's name and
|
|
// skip the translation phase.
|
|
//
|
|
|
|
p++;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The outer loop is executed each time a translation occurs.
|
|
//
|
|
|
|
while ( TRUE ) {
|
|
|
|
//
|
|
// Walk through any directory objects at the beginning of the string.
|
|
//
|
|
|
|
if ( *p != L'\\' ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
p++;
|
|
|
|
//
|
|
// The inner loop is executed while walking the directory tree.
|
|
//
|
|
|
|
while ( TRUE ) {
|
|
|
|
q = wcschr( p, L'\\' );
|
|
|
|
if ( q == NULL ) {
|
|
testString.Length = currentString.Length;
|
|
} else {
|
|
testString.Length = (USHORT)(q - currentString.Buffer) * sizeof(WCHAR);
|
|
}
|
|
testString.Buffer = currentString.Buffer;
|
|
remainingLength = currentString.Length - testString.Length + sizeof(WCHAR);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&testString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenDirectoryObject( &handle, DIRECTORY_TRAVERSE, &objectAttributes );
|
|
|
|
//
|
|
// If we were unable to open the object as a directory, then break out
|
|
// of the inner loop and try to open it as a symbolic link.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( status != STATUS_OBJECT_TYPE_MISMATCH ) {
|
|
goto error_exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We opened the directory. Close it and try the next element of the path.
|
|
//
|
|
|
|
ZwClose( handle );
|
|
|
|
if ( q == NULL ) {
|
|
|
|
//
|
|
// The last element of the name is an object directory. Clearly, this
|
|
// is not a redirector path.
|
|
//
|
|
|
|
status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
goto error_exit;
|
|
}
|
|
|
|
p = q + 1;
|
|
}
|
|
|
|
//
|
|
// Try to open the current name as a symbolic link.
|
|
//
|
|
|
|
status = ZwOpenSymbolicLinkObject( &handle, SYMBOLIC_LINK_QUERY, &objectAttributes );
|
|
|
|
//
|
|
// If we were unable to open the object as a symbolic link, then break out of
|
|
// the outer loop and verify that this is a redirector name.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if ( status != STATUS_OBJECT_TYPE_MISMATCH ) {
|
|
goto error_exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The object is a symbolic link. Translate it.
|
|
//
|
|
|
|
testString.MaximumLength = 0;
|
|
status = ZwQuerySymbolicLinkObject( handle, &testString, &translationLength );
|
|
if ( !NT_SUCCESS(status) && (status != STATUS_BUFFER_TOO_SMALL) ) {
|
|
ZwClose( handle );
|
|
goto error_exit;
|
|
}
|
|
|
|
resultLength = translationLength + remainingLength;
|
|
p = RxAllocatePoolWithTag( PagedPool|POOL_COLD_ALLOCATION, resultLength, RX_MISC_POOLTAG );
|
|
if ( p == NULL ) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ZwClose( handle );
|
|
goto error_exit;
|
|
}
|
|
|
|
testString.MaximumLength = (USHORT)translationLength;
|
|
testString.Buffer = p;
|
|
status = ZwQuerySymbolicLinkObject( handle, &testString, NULL );
|
|
ZwClose( handle );
|
|
if ( !NT_SUCCESS(status) ) {
|
|
RxFreePool( p );
|
|
goto error_exit;
|
|
}
|
|
if ( testString.Length > translationLength ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
RxFreePool( p );
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlCopyMemory( (PCHAR)p + testString.Length, q, remainingLength );
|
|
currentString.Buffer = p;
|
|
currentString.Length = (USHORT)(resultLength - sizeof(WCHAR));
|
|
currentString.MaximumLength = (USHORT)resultLength;
|
|
|
|
if ( translationBuffer != NULL ) {
|
|
RxFreePool( translationBuffer );
|
|
}
|
|
translationBuffer = p;
|
|
}
|
|
|
|
//
|
|
// We have a result name. Verify that it is a redirector name.
|
|
//
|
|
|
|
if ( !RtlPrefixUnicodeString( &RxDeviceObject->DeviceName, ¤tString, TRUE ) ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Skip over the redirector device name.
|
|
//
|
|
|
|
p = currentString.Buffer + (RxDeviceObject->DeviceName.Length / sizeof(WCHAR));
|
|
if ( *p != L'\\' ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Skip over the drive letter, if present.
|
|
//
|
|
|
|
if ( *(p + 1) == L';' ) {
|
|
p = wcschr( ++p, L'\\' );
|
|
if ( p == NULL ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Verify that the next part of the string is the correct net root name.
|
|
//
|
|
|
|
currentString.Length -= (USHORT)(p - currentString.Buffer) * sizeof(WCHAR);
|
|
currentString.Buffer = p;
|
|
|
|
if ( !RtlPrefixUnicodeString( NetRootName, ¤tString, TRUE ) ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
p += NetRootName->Length / sizeof(WCHAR);
|
|
if ( *p != L'\\' ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
p++;
|
|
if ( *p == 0 ) {
|
|
status = STATUS_OBJECT_NAME_INVALID;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Copy the rest of the string after the redirector name to a new buffer.
|
|
//
|
|
|
|
RtlCreateUnicodeString( RelativeName, p );
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
error_exit:
|
|
|
|
if ( translationBuffer != NULL ) {
|
|
RxFreePool( translationBuffer );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|