1883 lines
54 KiB
C
1883 lines
54 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbctl.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the IoControl and FsControl SMBs.
|
||
|
||
Transact2 Ioctl
|
||
Nt Transaction Io Control
|
||
|
||
Author:
|
||
|
||
Manny Weiser (mannyw) 10-Oct-91
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "smbioctl.tmh"
|
||
#pragma hdrstop
|
||
|
||
NTSTATUS
|
||
ProcessOs2Ioctl (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN USHORT Category,
|
||
IN USHORT Function,
|
||
IN PVOID Parameters,
|
||
IN ULONG InputParameterCount,
|
||
IN PULONG OutputParameterCount,
|
||
IN PVOID Data,
|
||
IN ULONG InputDataCount,
|
||
IN ULONG MaxOutputData,
|
||
IN PULONG OutputDataCount
|
||
);
|
||
|
||
VOID SRVFASTCALL
|
||
RestartNtIoctl (
|
||
IN PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvSmbIoctl )
|
||
#pragma alloc_text( PAGE, SrvSmbIoctlSecondary )
|
||
#pragma alloc_text( PAGE, RestartNtIoctl )
|
||
#pragma alloc_text( PAGE, SrvSmbIoctl2 )
|
||
#pragma alloc_text( PAGE, SrvSmbFsctl )
|
||
#pragma alloc_text( PAGE, ProcessOs2Ioctl )
|
||
#endif
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbIoctl (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a primary Ioctl SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
|
||
of the parameters to SMB processor routines.
|
||
|
||
Return Value:
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQ_IOCTL request;
|
||
PRESP_IOCTL response;
|
||
PSMB_HEADER header;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
||
|
||
PSESSION session;
|
||
PTREE_CONNECT treeConnect;
|
||
PRFCB rfcb;
|
||
|
||
CLONG parameterOffset;
|
||
CLONG parameterCount; // For input on this buffer
|
||
CLONG maxParameterCount; // For output
|
||
CLONG totalParameterCount; // For input
|
||
CLONG parameterSize; // Max of input and output parameter counts
|
||
CLONG dataOffset;
|
||
CLONG responseDataOffset;
|
||
CLONG dataCount; // For input on this buffer
|
||
CLONG maxDataCount; // For output
|
||
CLONG totalDataCount; // For input
|
||
CLONG dataSize; // Max of input and output data counts
|
||
|
||
CLONG smbLength;
|
||
CLONG numberOfPaddings = 0;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_IOCTL;
|
||
SrvWmiStartContext(WorkContext);
|
||
|
||
request = (PREQ_IOCTL)WorkContext->RequestParameters;
|
||
response = (PRESP_IOCTL)WorkContext->ResponseParameters;
|
||
|
||
//
|
||
// Since we do I/O from the SMB buffer, verify that the request and
|
||
// response buffers are one and the same.
|
||
//
|
||
|
||
ASSERT( (PVOID)request == (PVOID)response );
|
||
|
||
header = WorkContext->RequestHeader;
|
||
|
||
IF_SMB_DEBUG(TRANSACTION1) {
|
||
KdPrint(( "Ioctl (primary) request\n" ));
|
||
}
|
||
|
||
//
|
||
// !!! Verify ioctl subcommand early?
|
||
//
|
||
|
||
parameterOffset = SmbGetUshort( &request->ParameterOffset );
|
||
parameterCount = SmbGetUshort( &request->ParameterCount );
|
||
maxParameterCount = SmbGetUshort( &request->MaxParameterCount );
|
||
totalParameterCount = SmbGetUshort( &request->TotalParameterCount );
|
||
dataOffset = SmbGetUshort( &request->DataOffset );
|
||
dataCount = SmbGetUshort( &request->DataCount );
|
||
maxDataCount = SmbGetUshort( &request->MaxDataCount );
|
||
totalDataCount = SmbGetUshort( &request->TotalDataCount );
|
||
|
||
smbLength = WorkContext->RequestBuffer->DataLength;
|
||
|
||
dataSize = MAX( dataCount, maxDataCount );
|
||
parameterSize = MAX( parameterCount, maxParameterCount );
|
||
|
||
if ( parameterCount != 0 ) {
|
||
responseDataOffset = parameterOffset + parameterSize;
|
||
} else {
|
||
|
||
//
|
||
// Some ioctls requests have data offset of zero like
|
||
// category 0x53, function 0x60. If this is the case,
|
||
// calculate the dataoffset by hand.
|
||
//
|
||
|
||
if ( dataOffset != 0 ) {
|
||
responseDataOffset = dataOffset;
|
||
} else {
|
||
responseDataOffset = (CLONG) ((PUCHAR) response->Buffer -
|
||
(PUCHAR) WorkContext->ResponseHeader);
|
||
numberOfPaddings = ( responseDataOffset & 0x01 );
|
||
responseDataOffset = responseDataOffset + numberOfPaddings;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Verify the size of the smb buffer:
|
||
//
|
||
// Even though we know that WordCount and ByteCount are valid, it's
|
||
// still possible that the offsets and lengths of the Parameter and
|
||
// Data bytes are invalid. So we check them now.
|
||
//
|
||
// We need room in the smb buffer for the response. Ensure that
|
||
// there is enough room.
|
||
//
|
||
// No ioctl secondary is expected. Ensure that all data and
|
||
// parameters have arrrived.
|
||
//
|
||
// Check that the response will fit in a single buffer.
|
||
//
|
||
|
||
if ( ( (parameterOffset + parameterCount) > smbLength ) ||
|
||
( (dataOffset + dataCount) > smbLength ) ||
|
||
( (responseDataOffset + dataCount) >
|
||
WorkContext->ResponseBuffer->BufferLength ) ||
|
||
|
||
( dataCount != totalDataCount ) ||
|
||
( parameterCount != totalParameterCount ) ||
|
||
|
||
( (parameterOffset > dataOffset) && (dataCount != 0) ) ) {
|
||
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvSmbTransaction: Invalid parameter or data "
|
||
"offset+count: pOff=%ld,pCnt=%ld;",
|
||
parameterOffset, parameterCount ));
|
||
KdPrint(( "dOff=%ld,dCnt=%ld;", dataOffset, dataCount ));
|
||
KdPrint(( "smbLen=%ld", smbLength ));
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
||
status = STATUS_INVALID_SMB;
|
||
SmbStatus = SmbStatusSendResponse;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If a session block has not already been assigned to the current
|
||
// work context, verify the UID. If verified, the address of the
|
||
// session block corresponding to this user is stored in the
|
||
// WorkContext block and the session block is referenced.
|
||
//
|
||
// If a tree connect block has not already been assigned to the
|
||
// current work context, find the tree connect corresponding to the
|
||
// given TID.
|
||
//
|
||
|
||
status = SrvVerifyUidAndTid(
|
||
WorkContext,
|
||
&session,
|
||
&treeConnect,
|
||
ShareTypeWild
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvSmbIoctl: Invalid UID or TID\n" ));
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbStatusSendResponse;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( session->IsSessionExpired )
|
||
{
|
||
status = SESSION_EXPIRED_STATUS_CODE;
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbStatusSendResponse;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Verify the FID. If verified, the RFCB block is referenced
|
||
// and its addresses is stored in the WorkContext block, and the
|
||
// RFCB address is returned.
|
||
//
|
||
|
||
rfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
request->Fid,
|
||
TRUE,
|
||
SrvRestartSmbReceived, // serialize with raw write
|
||
&status
|
||
);
|
||
|
||
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// Invalid file ID or write behind error. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint((
|
||
"SrvSmbIoctl: Status %X on FID: 0x%lx\n",
|
||
request->Fid,
|
||
status
|
||
));
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbStatusSendResponse;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// The work item has been queued because a raw write is in
|
||
// progress.
|
||
//
|
||
|
||
SmbStatus = SmbStatusInProgress;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Make room in the SMB buffer for return parameters by copying data
|
||
//
|
||
|
||
if ( dataOffset != responseDataOffset && dataCount != 0) {
|
||
RtlMoveMemory(
|
||
(PCHAR)header + responseDataOffset,
|
||
(PCHAR)header + dataOffset,
|
||
dataCount
|
||
);
|
||
}
|
||
|
||
//
|
||
// Process the ioctl. The response will overwrite the request buffer.
|
||
//
|
||
|
||
status = ProcessOs2Ioctl(
|
||
WorkContext,
|
||
request->Category,
|
||
request->Function,
|
||
(PCHAR)WorkContext->RequestHeader + parameterOffset,
|
||
totalParameterCount,
|
||
&maxParameterCount,
|
||
(PCHAR)WorkContext->RequestHeader + responseDataOffset,
|
||
totalDataCount,
|
||
WorkContext->ResponseBuffer->BufferLength -
|
||
PTR_DIFF(WorkContext->RequestHeader,
|
||
WorkContext->ResponseBuffer->Buffer) -
|
||
responseDataOffset,
|
||
&maxDataCount
|
||
);
|
||
|
||
//
|
||
// Format and send the response, the parameter and data bytes are
|
||
// already in place.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbStatusSendResponse;
|
||
goto Cleanup;
|
||
}
|
||
|
||
response->WordCount = 8;
|
||
SmbPutUshort( &response->TotalParameterCount, (USHORT)maxParameterCount );
|
||
SmbPutUshort( &response->TotalDataCount, (USHORT)maxDataCount );
|
||
SmbPutUshort( &response->ParameterCount, (USHORT)maxParameterCount );
|
||
SmbPutUshort( &response->ParameterOffset, (USHORT)parameterOffset );
|
||
SmbPutUshort( &response->ParameterDisplacement, 0);
|
||
SmbPutUshort( &response->DataCount, (USHORT)maxDataCount );
|
||
SmbPutUshort( &response->DataOffset, (USHORT)responseDataOffset );
|
||
SmbPutUshort( &response->DataDisplacement, 0 );
|
||
|
||
SmbPutUshort(
|
||
&response->ByteCount,
|
||
(USHORT)(maxDataCount + numberOfPaddings)
|
||
);
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_IOCTL,
|
||
maxDataCount + numberOfPaddings
|
||
);
|
||
|
||
SmbStatus = SmbStatusSendResponse;
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatus;
|
||
|
||
} // SrvSmbIoctl
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbIoctlSecondary (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a secondary Ioctl SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
|
||
of the parameters to SMB processor routines.
|
||
|
||
Return Value:
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// This SMB is not supported.
|
||
//
|
||
|
||
SrvSetSmbError( WorkContext, STATUS_NOT_IMPLEMENTED );
|
||
return SmbStatusSendResponse;
|
||
}
|
||
|
||
|
||
SMB_TRANS_STATUS
|
||
SrvSmbNtIoctl (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a Nt Ioctl SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
|
||
of the parameters to SMB processor routines.
|
||
|
||
Return Value:
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
ULONG functionCode;
|
||
USHORT fid;
|
||
BOOLEAN isFsctl;
|
||
|
||
PREQ_NT_IO_CONTROL request;
|
||
|
||
PTRANSACTION transaction;
|
||
PRFCB rfcb;
|
||
PMDL mdl = NULL;
|
||
|
||
transaction = WorkContext->Parameters.Transaction;
|
||
|
||
request = (PREQ_NT_IO_CONTROL)transaction->InSetup;
|
||
|
||
functionCode = SmbGetAlignedUlong( &request->FunctionCode );
|
||
fid = SmbGetAlignedUshort( &request->Fid );
|
||
isFsctl = request->IsFsctl;
|
||
|
||
//
|
||
// Verify the FID. If verified, the RFCB block is referenced
|
||
// and its addresses is stored in the WorkContext block, and the
|
||
// RFCB address is returned.
|
||
//
|
||
|
||
rfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
fid,
|
||
TRUE,
|
||
SrvRestartExecuteTransaction, // serialize with raw write
|
||
&status
|
||
);
|
||
|
||
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// Invalid file ID or write behind error. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint((
|
||
"SrvSmbNtIoctl: Status %X on FID: 0x%lx\n",
|
||
status,
|
||
fid
|
||
));
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
return SmbTransStatusErrorWithoutData;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// The work item has been queued because a raw write is in
|
||
// progress.
|
||
//
|
||
|
||
return SmbTransStatusInProgress;
|
||
|
||
}
|
||
|
||
//
|
||
// Only allow these fellows against disk files
|
||
//
|
||
if( rfcb->ShareType != ShareTypeDisk ) {
|
||
SrvSetSmbError( WorkContext, STATUS_NOT_SUPPORTED );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
CHECK_FUNCTION_ACCESS(
|
||
rfcb->GrantedAccess,
|
||
(UCHAR)(isFsctl ? IRP_MJ_FILE_SYSTEM_CONTROL : IRP_MJ_DEVICE_CONTROL),
|
||
0,
|
||
functionCode,
|
||
&status
|
||
);
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
SrvStatistics.GrantedAccessErrors++;
|
||
SrvSetSmbError( WorkContext, status );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
|
||
//
|
||
// There are some functions which we cannot allow. Weed them out here
|
||
//
|
||
switch( functionCode ) {
|
||
case FSCTL_SET_REPARSE_POINT:
|
||
|
||
//
|
||
// If there's not enough data, or the structure is internally inconsistent --
|
||
// fail the request
|
||
//
|
||
status = STATUS_SUCCESS;
|
||
|
||
if( transaction->DataCount == 0 ) {
|
||
status = STATUS_INVALID_BUFFER_SIZE;
|
||
|
||
} else if ( transaction->DataCount < REPARSE_DATA_BUFFER_HEADER_SIZE ||
|
||
((PREPARSE_DATA_BUFFER)transaction->InData)->ReparseDataLength >
|
||
transaction->DataCount - FIELD_OFFSET( REPARSE_DATA_BUFFER, GenericReparseBuffer )
|
||
) {
|
||
status = STATUS_IO_REPARSE_DATA_INVALID;
|
||
}
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
//
|
||
// Only an administrator is allowed to set generalized reparse points,
|
||
// otherwise it is too easy to escape the share. This seems safer
|
||
// than a path check, and it also allows the administrator to point
|
||
// the reparse point to wherever s/he desires.
|
||
//
|
||
if( !SrvIsAdmin( WorkContext->Session->UserHandle ) ) {
|
||
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
break;
|
||
|
||
case FSCTL_SRV_ENUMERATE_SNAPSHOTS:
|
||
{
|
||
if( SrvProductTypeServer )
|
||
{
|
||
status = SrvSnapEnumerateSnapShots( WorkContext );
|
||
if( !NT_SUCCESS(status) )
|
||
{
|
||
SrvSetSmbError( WorkContext, status );
|
||
SrvCompleteExecuteTransaction( WorkContext, (status==STATUS_BUFFER_OVERFLOW)?SmbTransStatusErrorWithData:SmbTransStatusErrorWithoutData );
|
||
}
|
||
else
|
||
{
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusSuccess );
|
||
}
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
}
|
||
break;
|
||
|
||
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_MOVE_FILE:
|
||
case FSCTL_MARK_HANDLE:
|
||
case FSCTL_QUERY_RETRIEVAL_POINTERS:
|
||
case FSCTL_PIPE_ASSIGN_EVENT:
|
||
case FSCTL_GET_VOLUME_BITMAP:
|
||
case FSCTL_GET_NTFS_FILE_RECORD:
|
||
|
||
// We don't support USN journal calls because they require a volume handle
|
||
case FSCTL_READ_USN_JOURNAL:
|
||
case FSCTL_CREATE_USN_JOURNAL:
|
||
case FSCTL_QUERY_USN_JOURNAL:
|
||
case FSCTL_DELETE_USN_JOURNAL:
|
||
SrvSetSmbError( WorkContext, STATUS_NOT_SUPPORTED );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
if (functionCode == FSCTL_LMR_SET_LINK_TRACKING_INFORMATION) {
|
||
|
||
// This is a FSCTL that is used for link tracking purposes. It is
|
||
// an internal FSCTL issued by the I/O subsystem. Currently this
|
||
// is being handled in the context of a worker thread. We need
|
||
// to ensure that this arm of the code is only executed in the
|
||
// context of a blocking thread.
|
||
|
||
// Also note that the incoming structure will always be the 32-bit structure, even
|
||
// from 64-bit machines. All structures on the wire are 32-bit for backwards compatibility
|
||
|
||
KIRQL oldIrql;
|
||
PRFCB rfcbTarget = NULL;
|
||
USHORT TargetFid;
|
||
PVOID TargetHandle;
|
||
ULONG TargetInformationLength;
|
||
ULONG LinkTrackingInformationSize;
|
||
|
||
if( transaction->DataCount < sizeof( REMOTE_LINK_TRACKING_INFORMATION32 ) ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
TargetHandle = UlongToHandle( SmbGetUlong(
|
||
(PUCHAR)transaction->InData +
|
||
FIELD_OFFSET(
|
||
REMOTE_LINK_TRACKING_INFORMATION32,
|
||
TargetFileObject)) );
|
||
|
||
TargetInformationLength = SmbGetUlong(
|
||
(PUCHAR)transaction->InData +
|
||
FIELD_OFFSET(
|
||
REMOTE_LINK_TRACKING_INFORMATION32,
|
||
TargetLinkTrackingInformationLength));
|
||
|
||
LinkTrackingInformationSize = FIELD_OFFSET(
|
||
FILE_TRACKING_INFORMATION32,
|
||
ObjectInformation) +
|
||
TargetInformationLength;
|
||
|
||
//
|
||
// Make sure the REMOTE_LINK_TRACKING_INFORMATION structure is reasonable
|
||
//
|
||
if( TargetInformationLength > transaction->DataCount ||
|
||
LinkTrackingInformationSize > transaction->DataCount ) {
|
||
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
if (TargetHandle != NULL) {
|
||
|
||
TargetFid = (USHORT)TargetHandle;
|
||
|
||
//
|
||
// Verify the FID. This code is lifted from SrvVerifyFid2.
|
||
//
|
||
|
||
//
|
||
// Acquire the spin lock that guards the connection's file table.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK( &WorkContext->Connection->SpinLock, &oldIrql );
|
||
|
||
//
|
||
// See if this is the cached rfcb
|
||
//
|
||
|
||
if ( WorkContext->Connection->CachedFid == (ULONG)TargetFid ) {
|
||
|
||
rfcbTarget = WorkContext->Connection->CachedRfcb;
|
||
|
||
} else {
|
||
USHORT index;
|
||
USHORT sequence;
|
||
PTABLE_HEADER tableHeader;
|
||
|
||
//
|
||
// Verify that the FID is in range, is in use, and has the correct
|
||
// sequence number.
|
||
|
||
index = FID_INDEX( TargetFid );
|
||
sequence = FID_SEQUENCE( TargetFid );
|
||
tableHeader = &WorkContext->Connection->FileTable;
|
||
|
||
if ( (index < (USHORT)tableHeader->TableSize) &&
|
||
(tableHeader->Table[index].Owner != NULL) &&
|
||
(tableHeader->Table[index].SequenceNumber == sequence) &&
|
||
(GET_BLOCK_STATE(tableHeader->Table[index].Owner) == BlockStateActive) ) {
|
||
|
||
rfcbTarget = tableHeader->Table[index].Owner;
|
||
|
||
//
|
||
// Cache the fid.
|
||
//
|
||
|
||
WorkContext->Connection->CachedRfcb = rfcbTarget;
|
||
WorkContext->Connection->CachedFid = (ULONG)TargetFid;
|
||
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &WorkContext->Connection->SpinLock, oldIrql );
|
||
}
|
||
|
||
if( rfcbTarget != NULL || TargetHandle == NULL ) {
|
||
|
||
PFILE_TRACKING_INFORMATION pTrackingInformation;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
||
// Since the data in the InData buffer is unaligned we need to allocate
|
||
// a copy of the data that is aligned and copy the information over
|
||
// before passing it on in the SetInformationCall
|
||
// We also resize the buffer to the native structure on 64 bit (no change occurs on 32 bit)
|
||
|
||
pTrackingInformation = ALLOCATE_HEAP( LinkTrackingInformationSize + sizeof(PVOID) - sizeof(ULONG), BlockTypeMisc );
|
||
|
||
if (pTrackingInformation != NULL) {
|
||
if (rfcbTarget != NULL) {
|
||
pTrackingInformation->DestinationFile =
|
||
rfcbTarget->Lfcb->FileHandle;
|
||
} else {
|
||
pTrackingInformation->DestinationFile = NULL;
|
||
}
|
||
|
||
pTrackingInformation->ObjectInformationLength =
|
||
TargetInformationLength;
|
||
|
||
RtlCopyMemory(
|
||
pTrackingInformation->ObjectInformation,
|
||
((PUCHAR)transaction->InData +
|
||
FIELD_OFFSET(
|
||
REMOTE_LINK_TRACKING_INFORMATION,
|
||
TargetLinkTrackingInformationBuffer)),
|
||
TargetInformationLength);
|
||
|
||
status = NtSetInformationFile(
|
||
rfcb->Lfcb->FileHandle,
|
||
&ioStatusBlock,
|
||
pTrackingInformation,
|
||
LinkTrackingInformationSize,
|
||
FileTrackingInformation);
|
||
|
||
FREE_HEAP(pTrackingInformation);
|
||
} else {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
} else {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
SrvCompleteExecuteTransaction(
|
||
WorkContext,
|
||
SmbTransStatusErrorWithoutData
|
||
);
|
||
} else {
|
||
transaction->DataCount = 0;
|
||
transaction->ParameterCount = 0;
|
||
|
||
SrvCompleteExecuteTransaction(
|
||
WorkContext,
|
||
SmbTransStatusSuccess
|
||
);
|
||
}
|
||
|
||
return SmbTransStatusInProgress;
|
||
|
||
}
|
||
|
||
//
|
||
// Since we are doing ioctls to this file, it doesn't seem like it's
|
||
// a "normal" file. We had better not cache its handle after the close.
|
||
// Specifically, remote setting of the file's compression state is
|
||
// not reflected to the directory entry until the file is closed. And
|
||
// setting a file's compression state is done with an ioctl
|
||
//
|
||
rfcb->IsCacheable = FALSE;
|
||
|
||
if (functionCode == FSCTL_SIS_COPYFILE) {
|
||
|
||
//
|
||
// This the single-instance store copy FSCTL. We need to modify
|
||
// the file names, which are passed as share-relative names,
|
||
// to be full NT paths.
|
||
//
|
||
|
||
PSI_COPYFILE copyFile;
|
||
PSI_COPYFILE newCopyFile;
|
||
ULONG bufferLength;
|
||
PWCHAR source;
|
||
ULONG sourceLength;
|
||
PWCHAR dest;
|
||
ULONG destLength;
|
||
PSHARE share;
|
||
PWCHAR prefix;
|
||
ULONG prefixLength;
|
||
PCHAR p;
|
||
ULONG addSlashToSource;
|
||
ULONG addSlashToDest;
|
||
|
||
copyFile = (PSI_COPYFILE)transaction->InData;
|
||
bufferLength = transaction->DataCount;
|
||
|
||
if( bufferLength < sizeof( SI_COPYFILE ) ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
||
SrvCompleteExecuteTransaction( WorkContext, SmbTransStatusErrorWithoutData );
|
||
return SmbTransStatusInProgress;
|
||
}
|
||
|
||
//
|
||
// Get the share-relative paths.
|
||
//
|
||
|
||
source = copyFile->FileNameBuffer;
|
||
sourceLength = copyFile->SourceFileNameLength;
|
||
dest = source + (sourceLength / sizeof(WCHAR));
|
||
destLength = copyFile->DestinationFileNameLength;
|
||
|
||
//
|
||
// Verify that the input structure is reasonable.
|
||
//
|
||
|
||
if ( (sourceLength > bufferLength || sourceLength == 0 ) ||
|
||
(destLength > bufferLength || destLength == 0 ) ||
|
||
((FIELD_OFFSET(SI_COPYFILE,FileNameBuffer) + sourceLength + destLength) > bufferLength) ||
|
||
(*(source + (sourceLength/sizeof(WCHAR)-1)) != 0) ||
|
||
(*(dest + (destLength/sizeof(WCHAR)-1)) != 0) ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_PARAMETER );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
|
||
ASSERT( (FSCTL_SIS_COPYFILE & 3) == METHOD_BUFFERED );
|
||
|
||
//
|
||
// Get the NT path prefix for the share.
|
||
//
|
||
|
||
share = WorkContext->TreeConnect->Share;
|
||
prefix = share->NtPathName.Buffer;
|
||
prefixLength = share->NtPathName.Length;
|
||
|
||
//
|
||
// Make sure there is exactly one slash between the share prefix
|
||
// and the paths in the request.
|
||
//
|
||
|
||
addSlashToSource = 0;
|
||
addSlashToDest = 0;
|
||
if ( IS_UNICODE_PATH_SEPARATOR(*(prefix + (prefixLength/sizeof(WCHAR)-1))) ) {
|
||
if ( IS_UNICODE_PATH_SEPARATOR(*source) ) {
|
||
source++;
|
||
sourceLength -= sizeof(WCHAR);
|
||
}
|
||
if ( IS_UNICODE_PATH_SEPARATOR(*dest) ) {
|
||
dest++;
|
||
destLength -= sizeof(WCHAR);
|
||
}
|
||
} else {
|
||
if ( !IS_UNICODE_PATH_SEPARATOR(*source) ) {
|
||
addSlashToSource = sizeof(WCHAR);
|
||
}
|
||
if ( !IS_UNICODE_PATH_SEPARATOR(*dest) ) {
|
||
addSlashToDest = sizeof(WCHAR);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate space for a new FSCTL command buffer.
|
||
//
|
||
|
||
bufferLength = FIELD_OFFSET(SI_COPYFILE,FileNameBuffer) +
|
||
prefixLength + addSlashToSource + sourceLength +
|
||
prefixLength + addSlashToSource + destLength;
|
||
newCopyFile = ALLOCATE_HEAP( bufferLength, BlockTypeBuffer );
|
||
|
||
if( newCopyFile == NULL ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
|
||
//
|
||
// Initialize the FSCTL input buffer with the full NT path names.
|
||
//
|
||
|
||
newCopyFile->SourceFileNameLength = prefixLength + addSlashToSource + sourceLength;
|
||
newCopyFile->DestinationFileNameLength = prefixLength + addSlashToDest + destLength;
|
||
newCopyFile->Flags = copyFile->Flags;
|
||
p = (PCHAR)newCopyFile->FileNameBuffer;
|
||
RtlCopyMemory( p, prefix, prefixLength );
|
||
p += prefixLength;
|
||
if ( addSlashToSource != 0 ) {
|
||
*(PWCHAR)p = UNICODE_DIR_SEPARATOR_CHAR;
|
||
p += sizeof(WCHAR);
|
||
}
|
||
RtlCopyMemory( p, source, sourceLength );
|
||
p += sourceLength;
|
||
RtlCopyMemory( p, prefix, prefixLength );
|
||
p += prefixLength;
|
||
if ( addSlashToDest != 0 ) {
|
||
*(PWCHAR)p = UNICODE_DIR_SEPARATOR_CHAR;
|
||
p += sizeof(WCHAR);
|
||
}
|
||
RtlCopyMemory( p, dest, destLength );
|
||
|
||
//
|
||
// Modify the transaction to point to the new buffer and indicate
|
||
// that the buffer should be freed when the transaction is done.
|
||
// (Note that the original buffer was allocated as part of the
|
||
// transaction block and doesn't need to be freed separately.)
|
||
//
|
||
|
||
transaction->InData = (PVOID)newCopyFile;
|
||
transaction->OutData = (PVOID)newCopyFile;
|
||
transaction->DataCount = bufferLength;
|
||
transaction->OutputBufferCopied = TRUE;
|
||
transaction->OutDataAllocated = TRUE;
|
||
}
|
||
|
||
switch( functionCode & 3 ) {
|
||
case METHOD_IN_DIRECT:
|
||
case METHOD_OUT_DIRECT:
|
||
|
||
if( transaction->TotalDataCount ) {
|
||
//
|
||
// Need an mdl
|
||
//
|
||
status = STATUS_SUCCESS;
|
||
|
||
mdl = IoAllocateMdl(
|
||
transaction->InData,
|
||
transaction->TotalDataCount,
|
||
FALSE,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
|
||
if ( mdl == NULL ) {
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
} else {
|
||
|
||
//
|
||
// Build the mdl
|
||
//
|
||
|
||
try {
|
||
MmProbeAndLockPages(
|
||
mdl,
|
||
KernelMode,
|
||
IoReadAccess
|
||
);
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
status = GetExceptionCode();
|
||
IoFreeMdl( mdl );
|
||
mdl = NULL;
|
||
}
|
||
}
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case METHOD_NEITHER:
|
||
//
|
||
// We need to allocate the output buffer for this fsctl, because at
|
||
// this point both the input and output buffers point to the same
|
||
// region of memory. This can't be guaranteed to work for METHOD_NEITHER
|
||
//
|
||
|
||
if( transaction->MaxDataCount ) {
|
||
//
|
||
// Let's not let the allocation get out of hand!
|
||
//
|
||
if( transaction->MaxDataCount > SrvMaxFsctlBufferSize ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
|
||
transaction->OutData = ALLOCATE_HEAP( transaction->MaxDataCount, BlockTypeBuffer );
|
||
|
||
if( transaction->OutData == NULL ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return SmbTransStatusErrorWithoutData;
|
||
}
|
||
|
||
RtlZeroMemory( transaction->OutData, transaction->MaxDataCount );
|
||
transaction->OutputBufferCopied = TRUE;
|
||
transaction->OutDataAllocated = TRUE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Set the Restart Routine addresses in the work context block.
|
||
//
|
||
|
||
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
||
WorkContext->FspRestartRoutine = RestartNtIoctl;
|
||
|
||
//
|
||
// Build the IRP to start the I/O control.
|
||
// Pass this request to the filesystem.
|
||
//
|
||
|
||
SrvBuildIoControlRequest(
|
||
WorkContext->Irp,
|
||
rfcb->Lfcb->FileObject,
|
||
WorkContext,
|
||
(UCHAR)(isFsctl ? IRP_MJ_FILE_SYSTEM_CONTROL : IRP_MJ_DEVICE_CONTROL),
|
||
functionCode,
|
||
transaction->InData,
|
||
transaction->DataCount,
|
||
transaction->OutData,
|
||
transaction->MaxDataCount,
|
||
mdl,
|
||
NULL // Completion routine
|
||
);
|
||
|
||
(VOID)IoCallDriver(
|
||
IoGetRelatedDeviceObject(rfcb->Lfcb->FileObject ),
|
||
WorkContext->Irp
|
||
);
|
||
|
||
//
|
||
// The call was successfully started, return InProgress to the caller
|
||
//
|
||
|
||
return SmbTransStatusInProgress;
|
||
|
||
} // SrvSmbNtIoctl
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
RestartNtIoctl (
|
||
IN PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function handles the completion of an NT Io control SMB.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - A pointer to a WORK_CONTEXT block.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
ULONG length;
|
||
PTRANSACTION transaction;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Free the MDL if one was allocated.
|
||
//
|
||
|
||
if ( WorkContext->Irp->MdlAddress != NULL ) {
|
||
MmUnlockPages( WorkContext->Irp->MdlAddress );
|
||
IoFreeMdl( WorkContext->Irp->MdlAddress );
|
||
WorkContext->Irp->MdlAddress = NULL;
|
||
}
|
||
|
||
//
|
||
// If the Io Control request failed, set an error status in the response
|
||
// header.
|
||
//
|
||
|
||
status = WorkContext->Irp->IoStatus.Status;
|
||
|
||
if ( NT_ERROR(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "RestartNtIoctl: Io control failed: %X\n",
|
||
status ));
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
|
||
SrvCompleteExecuteTransaction(
|
||
WorkContext,
|
||
SmbTransStatusErrorWithoutData
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Success. Prepare to generate and send the response.
|
||
//
|
||
|
||
transaction = WorkContext->Parameters.Transaction;
|
||
|
||
length = MIN( (ULONG)WorkContext->Irp->IoStatus.Information, transaction->MaxDataCount );
|
||
|
||
if ( transaction->MaxSetupCount > 0 ) {
|
||
transaction->SetupCount = 1;
|
||
SmbPutUshort( transaction->OutSetup, (USHORT)length );
|
||
}
|
||
|
||
transaction->ParameterCount = transaction->MaxParameterCount;
|
||
transaction->DataCount = length;
|
||
|
||
if (!NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "RestartNtIoctl: Io control failed: %X\n",
|
||
status ));
|
||
}
|
||
SrvSetSmbError2( WorkContext, status, TRUE );
|
||
|
||
SrvCompleteExecuteTransaction(
|
||
WorkContext,
|
||
SmbTransStatusErrorWithData
|
||
);
|
||
} else {
|
||
SrvCompleteExecuteTransaction(
|
||
WorkContext,
|
||
SmbTransStatusSuccess);
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // RestartNtIoctl
|
||
|
||
|
||
SMB_TRANS_STATUS
|
||
SrvSmbIoctl2 (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the Ioctl request. This request arrives in a Transaction2 SMB.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies the address of a Work Context Block
|
||
describing the current request. See smbtypes.h for a more
|
||
complete description of the valid fields.
|
||
|
||
Return Value:
|
||
|
||
SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
|
||
whether data should be returned to the client. See smbtypes.h
|
||
for a more complete description.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
|
||
PTRANSACTION transaction;
|
||
PRFCB rfcb;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_IOCTL2;
|
||
SrvWmiStartContext(WorkContext);
|
||
|
||
transaction = WorkContext->Parameters.Transaction;
|
||
IF_SMB_DEBUG(TRANSACTION2) {
|
||
KdPrint(( "Ioctl2 entered; transaction 0x%p\n",
|
||
transaction ));
|
||
}
|
||
|
||
//request = (PREQ_IOCTL2)transaction->InSetup;
|
||
|
||
//
|
||
// Verify the setup count.
|
||
//
|
||
|
||
if ( transaction->SetupCount != 4 * sizeof( USHORT ) ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
||
status = STATUS_INVALID_SMB;
|
||
SmbStatus = SmbTransStatusErrorWithoutData;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Verify the FID. If verified, the RFCB block is referenced
|
||
// and its addresses is stored in the WorkContext block, and the
|
||
// RFCB address is returned.
|
||
//
|
||
|
||
rfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
transaction->InSetup[1],
|
||
TRUE,
|
||
SrvRestartExecuteTransaction, // serialize with raw write
|
||
&status
|
||
);
|
||
|
||
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// Invalid file ID or write behind error. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint((
|
||
"SrvSmbIoctl2: Status %X on FID: 0x%lx\n",
|
||
transaction->InSetup[1],
|
||
status
|
||
));
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbTransStatusErrorWithoutData;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// The work item has been queued because a raw write is in
|
||
// progress.
|
||
//
|
||
|
||
SmbStatus = SmbTransStatusInProgress;
|
||
goto Cleanup;
|
||
}
|
||
|
||
transaction->Category = transaction->InSetup[2];
|
||
transaction->Function = transaction->InSetup[3];
|
||
|
||
//
|
||
// Perform the Ioctl
|
||
//
|
||
|
||
status = ProcessOs2Ioctl(
|
||
WorkContext,
|
||
transaction->InSetup[2],
|
||
transaction->InSetup[3],
|
||
transaction->InParameters,
|
||
transaction->ParameterCount,
|
||
&transaction->MaxParameterCount,
|
||
transaction->OutData,
|
||
transaction->DataCount,
|
||
transaction->MaxDataCount,
|
||
&transaction->MaxDataCount
|
||
);
|
||
|
||
//
|
||
// If an error occurred, return an appropriate response.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbTransStatusErrorWithoutData;
|
||
goto Cleanup;
|
||
}
|
||
|
||
transaction->SetupCount = 0;
|
||
transaction->ParameterCount = transaction->MaxParameterCount;
|
||
transaction->DataCount = transaction->MaxDataCount;
|
||
SmbStatus = SmbTransStatusSuccess;
|
||
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbIoctl2 complete.\n" ));
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatus;
|
||
|
||
} // SrvSmbIoctl2
|
||
|
||
|
||
SMB_TRANS_STATUS
|
||
SrvSmbFsctl (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// The OS/2 redirector never sends a remote FS control request.
|
||
// If we get one, simply reply that we cannot handle it.
|
||
//
|
||
|
||
return SrvTransactionNotImplemented( WorkContext );
|
||
|
||
} // SrvSmbFsctl
|
||
|
||
|
||
#define SERIAL_DEVICE 0x1
|
||
#define PRINTER_DEVICE 0x5
|
||
#define GENERAL_DEVICE 0xB
|
||
#define SPOOLER_DEVICE 0x53
|
||
|
||
//
|
||
// Serial device functions supported
|
||
//
|
||
|
||
#define SET_BAUD_RATE 0x41
|
||
#define SET_LINE_CONTROL 0x42
|
||
#define SET_TRANSMIT_TIMEOUT 0x44
|
||
#define SET_BREAK_OFF 0x45
|
||
#define SET_MODEM_CONTROL 0x46
|
||
#define SET_BREAK_ON 0x4B
|
||
#define STOP_TRANSMIT 0x47
|
||
#define START_TRANSMIT 0x48
|
||
#define SET_DCB_INFORMATION 0x53
|
||
#define GET_BAUD_RATE 0x61
|
||
#define GET_LINE_CONTROL 0x62
|
||
#define GET_COMM_STATUS 0x64
|
||
#define GET_LINE_STATUS 0x65
|
||
#define GET_MODEM_OUTPUT 0x66
|
||
#define GET_MODEM_INPUT 0x67
|
||
#define GET_INQUEUE_COUNT 0x68
|
||
#define GET_OUTQUEUE_COUNT 0x69
|
||
#define GET_COMM_ERROR 0x6D
|
||
#define GET_COMM_EVENT 0x72
|
||
#define GET_DCB_INFORMATION 0x73
|
||
|
||
//
|
||
// Print device function supported.
|
||
//
|
||
// *** Note: The OS/2 server supports 2 additional Ioctl functions.
|
||
// ActivateFont (0x48) and QueryActiveFont (0x69). Since these
|
||
// were designed only to support IBM proplus printer from OS/2
|
||
// and we can't correctly support these function, we don't.
|
||
//
|
||
|
||
#define GET_PRINTER_ID 0x60
|
||
#define GET_PRINTER_STATUS 0x66
|
||
|
||
#define OS2_STATUS_PRINTER_HAPPY 0x90
|
||
|
||
typedef struct _SMB_IOCTL_LINECONTROL {
|
||
UCHAR DataBits;
|
||
UCHAR Parity;
|
||
UCHAR StopBits;
|
||
UCHAR TransBreak;
|
||
} SMB_IOCTL_LINE_CONTROL, *PSMB_IOCTL_LINE_CONTROL;
|
||
|
||
typedef struct _SMB_IOCTL_BAUD_RATE {
|
||
USHORT BaudRate;
|
||
} SMB_IOCTL_BAUD_RATE, *PSMB_IOCTL_BAUD_RATE;
|
||
|
||
typedef struct _SMB_IOCTL_DEVICE_CONTROL {
|
||
USHORT WriteTimeout;
|
||
USHORT ReadTimeout;
|
||
UCHAR ControlHandShake;
|
||
UCHAR FlowReplace;
|
||
UCHAR Timeout;
|
||
UCHAR ErrorReplacementChar;
|
||
UCHAR BreakReplacementChar;
|
||
UCHAR XonChar;
|
||
UCHAR XoffChar;
|
||
} SMB_IOCTL_DEVICE_CONTROL, *PSMB_IOCTL_DEVICE_CONTROL;
|
||
|
||
typedef struct _SMB_IOCTL_COMM_ERROR {
|
||
USHORT Error;
|
||
} SMB_IOCTL_COMM_ERROR, *PSMB_IOCTL_COMM_ERROR;
|
||
|
||
typedef struct _SMB_IOCTL_PRINTER_ID {
|
||
USHORT JobId;
|
||
UCHAR Buffer[1]; // server name and share name
|
||
} SMB_IOCTL_PRINTER_ID;
|
||
|
||
typedef SMB_IOCTL_PRINTER_ID SMB_UNALIGNED *PSMB_IOCTL_PRINTER_ID;
|
||
|
||
NTSTATUS
|
||
ProcessOs2Ioctl (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN USHORT Category,
|
||
IN USHORT Function,
|
||
IN PVOID Parameters,
|
||
IN ULONG InputParameterCount,
|
||
IN OUT PULONG OutputParameterCount,
|
||
IN PVOID Data,
|
||
IN ULONG InputDataCount,
|
||
IN ULONG MaxOutputData,
|
||
IN OUT PULONG OutputDataCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function handles an OS/2 ioctl. It convert the Ioctl SMB data
|
||
into an NT ioctl call, makes the call, and format the returned data
|
||
into Ioctl SMB return data.
|
||
|
||
Arguments:
|
||
|
||
WorkContext
|
||
Category
|
||
Function
|
||
Parameters
|
||
InputParameterCount
|
||
OutputParameterCount
|
||
Data
|
||
InputDataCount
|
||
OutputDataCount
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
NTSTATUS status;
|
||
PCHAR buffer;
|
||
PLFCB lfcb = WorkContext->Rfcb->Lfcb;
|
||
HANDLE Handle = lfcb->FileHandle;
|
||
|
||
union NT_PARAMTERS {
|
||
SERIAL_BAUD_RATE BaudRate;
|
||
SERIAL_LINE_CONTROL LineControl;
|
||
SERIAL_TIMEOUTS Timeouts;
|
||
SERIAL_QUEUE_SIZE QueueSize;
|
||
ULONG WaitMask;
|
||
ULONG PurgeMask;
|
||
UCHAR ImmediateChar;
|
||
UCHAR Reserved[3];
|
||
SERIAL_CHARS Chars;
|
||
SERIAL_HANDFLOW Handflow;
|
||
SERIAL_STATUS SerialStatus;
|
||
} ntBuffer;
|
||
|
||
union SMB_PARAMETERS {
|
||
PSMB_IOCTL_BAUD_RATE BaudRate;
|
||
PSMB_IOCTL_LINE_CONTROL LineControl;
|
||
PSMB_IOCTL_DEVICE_CONTROL DeviceControl;
|
||
PSMB_IOCTL_COMM_ERROR CommError;
|
||
PSMB_IOCTL_PRINTER_ID PrinterId;
|
||
} smbParameters, smbData;
|
||
|
||
PAGED_CODE( );
|
||
|
||
InputParameterCount, InputDataCount;
|
||
|
||
switch ( Category ) {
|
||
|
||
case SERIAL_DEVICE:
|
||
switch ( Function ) {
|
||
|
||
case GET_BAUD_RATE:
|
||
if( MaxOutputData < sizeof( SMB_IOCTL_BAUD_RATE ) ) {
|
||
status = STATUS_INVALID_SMB;
|
||
break;
|
||
}
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_GET_BAUD_RATE,
|
||
NULL,
|
||
0,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_BAUD_RATE )
|
||
);
|
||
|
||
//
|
||
// Convert the response to OS/2 format.
|
||
//
|
||
// !!! ULONG to USHORT conversion.
|
||
//
|
||
|
||
smbData.BaudRate = (PSMB_IOCTL_BAUD_RATE)Data;
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
smbData.BaudRate->BaudRate = (USHORT) ntBuffer.BaudRate.BaudRate;
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = sizeof( SMB_IOCTL_BAUD_RATE );
|
||
}
|
||
|
||
break;
|
||
|
||
case SET_BAUD_RATE:
|
||
|
||
//
|
||
// Convert the request to NT format.
|
||
//
|
||
|
||
smbParameters.BaudRate =
|
||
(PSMB_IOCTL_BAUD_RATE)Parameters;
|
||
|
||
ntBuffer.BaudRate.BaudRate = smbParameters.BaudRate->BaudRate;
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_SET_BAUD_RATE,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_BAUD_RATE ),
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
|
||
break;
|
||
|
||
case SET_LINE_CONTROL:
|
||
|
||
//
|
||
// Convert the request to NT format.
|
||
//
|
||
|
||
smbParameters.LineControl =
|
||
(PSMB_IOCTL_LINE_CONTROL)Parameters;
|
||
|
||
ntBuffer.LineControl.StopBits = smbParameters.LineControl->StopBits;
|
||
ntBuffer.LineControl.Parity = smbParameters.LineControl->Parity;
|
||
ntBuffer.LineControl.WordLength = smbParameters.LineControl->DataBits;
|
||
|
||
// !!! What about TransmitBreak?
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_SET_LINE_CONTROL,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_LINE_CONTROL ),
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
|
||
break;
|
||
|
||
case GET_LINE_CONTROL:
|
||
|
||
if( MaxOutputData < sizeof( SMB_IOCTL_LINE_CONTROL ) ) {
|
||
status = STATUS_INVALID_SMB;
|
||
break;
|
||
}
|
||
|
||
smbData.LineControl = (PSMB_IOCTL_LINE_CONTROL)Data;
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_GET_LINE_CONTROL,
|
||
NULL,
|
||
0,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_LINE_CONTROL )
|
||
);
|
||
|
||
//
|
||
// Convert the response to OS/2 format.
|
||
//
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
smbData.LineControl->DataBits = ntBuffer.LineControl.WordLength;
|
||
smbData.LineControl->Parity = ntBuffer.LineControl.Parity;
|
||
smbData.LineControl->StopBits = ntBuffer.LineControl.StopBits;
|
||
smbData.LineControl->TransBreak = 0; // !!!
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = sizeof( SMB_IOCTL_LINE_CONTROL );
|
||
}
|
||
|
||
break;
|
||
|
||
case GET_DCB_INFORMATION:
|
||
|
||
if( MaxOutputData < sizeof( SMB_IOCTL_DEVICE_CONTROL ) ) {
|
||
status = STATUS_INVALID_SMB;
|
||
break;
|
||
}
|
||
|
||
smbData.DeviceControl =
|
||
(PSMB_IOCTL_DEVICE_CONTROL)Data;
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_GET_TIMEOUTS,
|
||
NULL,
|
||
0,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_TIMEOUTS )
|
||
);
|
||
|
||
//
|
||
// Convert the response to OS/2 format.
|
||
//
|
||
|
||
// !!! Verify units are correct
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
smbData.DeviceControl->WriteTimeout = (USHORT)ntBuffer.Timeouts.ReadIntervalTimeout; // !!!
|
||
smbData.DeviceControl->ReadTimeout = (USHORT)ntBuffer.Timeouts.ReadIntervalTimeout;
|
||
} else {
|
||
break;
|
||
}
|
||
|
||
status = NtDeviceIoControlFile(
|
||
Handle,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&ioStatusBlock,
|
||
IOCTL_SERIAL_GET_TIMEOUTS,
|
||
NULL,
|
||
0,
|
||
&ntBuffer,
|
||
sizeof( SERIAL_TIMEOUTS )
|
||
);
|
||
|
||
//
|
||
// Convert the response to OS/2 format.
|
||
//
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
smbData.DeviceControl->XonChar = ntBuffer.Chars.XonChar;
|
||
smbData.DeviceControl->XoffChar = ntBuffer.Chars.XoffChar;
|
||
smbData.DeviceControl->ErrorReplacementChar = ntBuffer.Chars.ErrorChar;
|
||
smbData.DeviceControl->BreakReplacementChar = ntBuffer.Chars.BreakChar;
|
||
} else {
|
||
break;
|
||
}
|
||
|
||
smbData.DeviceControl->ControlHandShake = 0; // !!!
|
||
smbData.DeviceControl->FlowReplace = 0; // !!!
|
||
smbData.DeviceControl->Timeout = 0; // !!!
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = sizeof( SMB_IOCTL_DEVICE_CONTROL );
|
||
|
||
break;
|
||
|
||
case SET_DCB_INFORMATION:
|
||
|
||
//
|
||
// Lie. Pretend this succeeded.
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
break;
|
||
|
||
case GET_COMM_ERROR:
|
||
|
||
//
|
||
// Pretend that there is no comm error.
|
||
//
|
||
|
||
smbData.CommError = (PSMB_IOCTL_COMM_ERROR)Data;
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
smbData.CommError->Error = 0;
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = sizeof( SMB_IOCTL_COMM_ERROR );
|
||
}
|
||
|
||
break;
|
||
|
||
case SET_TRANSMIT_TIMEOUT:
|
||
case SET_BREAK_OFF:
|
||
case SET_MODEM_CONTROL:
|
||
case SET_BREAK_ON:
|
||
case STOP_TRANSMIT:
|
||
case START_TRANSMIT:
|
||
case GET_COMM_STATUS:
|
||
case GET_LINE_STATUS:
|
||
case GET_MODEM_OUTPUT:
|
||
case GET_MODEM_INPUT:
|
||
case GET_INQUEUE_COUNT:
|
||
case GET_OUTQUEUE_COUNT:
|
||
case GET_COMM_EVENT:
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
break;
|
||
|
||
default:
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
case PRINTER_DEVICE:
|
||
IF_SMB_DEBUG( TRANSACTION2 ) {
|
||
KdPrint(( "ProcessOs2Ioctl: print IOCTL function %lx received.\n",
|
||
Function ));
|
||
}
|
||
|
||
switch ( Function ) {
|
||
|
||
case GET_PRINTER_STATUS:
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
|
||
if ( *(PCHAR)Parameters != 0 ) {
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Always return STATUS_PRINTER_HAPPY
|
||
//
|
||
|
||
if( MaxOutputData < sizeof( CHAR ) ) {
|
||
status = STATUS_INVALID_SMB;
|
||
} else {
|
||
*(PCHAR)Data = (CHAR)OS2_STATUS_PRINTER_HAPPY;
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = sizeof( CHAR );
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
status = STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
break;
|
||
|
||
|
||
case SPOOLER_DEVICE:
|
||
IF_SMB_DEBUG( TRANSACTION2 ) {
|
||
KdPrint(( "ProcessOs2Ioctl: spool IOCTL function %lx received.\n",
|
||
Function ));
|
||
}
|
||
|
||
switch ( Function ) {
|
||
|
||
case GET_PRINTER_ID:
|
||
|
||
{
|
||
PUNICODE_STRING shareName = &WorkContext->TreeConnect->Share->ShareName;
|
||
OEM_STRING ansiShare;
|
||
|
||
if( MaxOutputData < 2 * (LM20_CNLEN + 1) ) {
|
||
status = STATUS_INVALID_SMB;
|
||
break;
|
||
}
|
||
|
||
smbData.PrinterId = (PSMB_IOCTL_PRINTER_ID) Data;
|
||
smbData.PrinterId->JobId = (USHORT)lfcb->JobId;
|
||
|
||
buffer = (PCHAR)smbData.PrinterId->Buffer;
|
||
|
||
if ( WorkContext->Connection->Endpoint->TransportAddress.Buffer != NULL ) {
|
||
RtlCopyMemory(
|
||
buffer,
|
||
WorkContext->Connection->Endpoint->TransportAddress.Buffer,
|
||
MIN(WorkContext->Connection->Endpoint->TransportAddress.Length+1,LM20_CNLEN)
|
||
);
|
||
} else {
|
||
*buffer = '\0';
|
||
}
|
||
|
||
buffer += LM20_CNLEN;
|
||
*buffer++ = '\0';
|
||
|
||
status = RtlUnicodeStringToOemString(
|
||
&ansiShare,
|
||
shareName,
|
||
TRUE
|
||
);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
|
||
if ( ansiShare.Length >= LM20_NNLEN ) {
|
||
RtlCopyMemory(
|
||
buffer,
|
||
ansiShare.Buffer,
|
||
LM20_NNLEN
|
||
);
|
||
} else {
|
||
RtlCopyMemory(
|
||
buffer,
|
||
ansiShare.Buffer,
|
||
ansiShare.Length + 1
|
||
);
|
||
|
||
}
|
||
|
||
RtlFreeAnsiString(&ansiShare);
|
||
|
||
} else {
|
||
|
||
*buffer = '\0';
|
||
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
buffer += LM20_NNLEN;
|
||
*buffer++ = '\0';
|
||
|
||
*OutputParameterCount = 0;
|
||
|
||
//
|
||
// data length is equal to the job id +
|
||
// the computer name + the share name + 1
|
||
// I don't know what the last + 1 is for but OS/2
|
||
// sends it.
|
||
//
|
||
|
||
*OutputDataCount = sizeof(USHORT) + LM20_CNLEN + 1 +
|
||
LM20_NNLEN + 2;
|
||
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
default:
|
||
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
status = STATUS_NOT_SUPPORTED;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
case GENERAL_DEVICE:
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
break;
|
||
|
||
default:
|
||
|
||
// for OS/2 1.x compatibility
|
||
|
||
status = STATUS_SUCCESS;
|
||
*OutputParameterCount = 0;
|
||
*OutputDataCount = 0;
|
||
}
|
||
|
||
IF_SMB_DEBUG( TRANSACTION2 ) {
|
||
|
||
KdPrint( (
|
||
"Category %x, Function %x returns %lx\n",
|
||
Category,
|
||
Function,
|
||
status
|
||
));
|
||
}
|
||
|
||
return status;
|
||
|
||
} // ProcessOs2Ioctl
|