windows-nt/Source/XPSP1/NT/base/fs/srv/smbmisc.c
2020-09-26 16:20:57 +08:00

2351 lines
68 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbmisc.c
Abstract:
This module contains routines for processing MISC class SMBs:
Echo
Query FS Information
Set FS Information
Query Disk Information
Author:
Chuck Lenzmeier (chuckl) 9-Nov-1989
David Treadwell (davidtr)
Revision History:
--*/
#include "precomp.h"
#include "smbmisc.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBMISC
STATIC
ULONG QueryVolumeInformation[] = {
SMB_QUERY_FS_LABEL_INFO, // Base level
FileFsLabelInformation, // Mapping for base level
FileFsVolumeInformation,
FileFsSizeInformation,
FileFsDeviceInformation,
FileFsAttributeInformation
};
STATIC
VOID SRVFASTCALL
RestartEcho (
IN OUT PWORK_CONTEXT WorkContext
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbEcho )
#pragma alloc_text( PAGE, RestartEcho )
#pragma alloc_text( PAGE, SrvSmbQueryFsInformation )
#pragma alloc_text( PAGE, SrvSmbSetFsInformation )
#pragma alloc_text( PAGE, SrvSmbQueryInformationDisk )
#pragma alloc_text( PAGE, SrvSmbSetSecurityDescriptor )
#pragma alloc_text( PAGE, SrvSmbQuerySecurityDescriptor )
#pragma alloc_text( PAGE, SrvSmbQueryQuota )
#pragma alloc_text( PAGE, SrvSmbSetQuota )
#endif
#if 0
NOT PAGEABLE -- SrvSmbNtCancel
#endif
SMB_PROCESSOR_RETURN_TYPE
SrvSmbEcho (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes an Echo SMB. It sends the first echo, if any, specifying
RestartEcho as the restart routine. That routine sends the
remaining echoes.
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
--*/
{
PREQ_ECHO request;
PRESP_ECHO response;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_ECHO;
SrvWmiStartContext(WorkContext);
request = (PREQ_ECHO)WorkContext->RequestParameters;
response = (PRESP_ECHO)WorkContext->ResponseParameters;
//
// If the echo count is 0, there are no echoes to send.
//
if ( SmbGetUshort( &request->EchoCount ) == 0 ) {
SmbStatus = SmbStatusNoResponse;
goto Cleanup;
}
//
// The echo count is not zero. Save it in the work context, then
// send the first echo.
//
// *** This code depends on the response buffer being the same as
// the request buffer. It does not copy the echo data from the
// request to the response. It does not update the DataLength
// of the response buffer.
//
// !!! Need to put in code to verify the requested TID, if any.
//
SrvReleaseContext( WorkContext );
WorkContext->Parameters.RemainingEchoCount =
(USHORT)(SmbGetUshort( &request->EchoCount ) - 1);
ASSERT( WorkContext->ResponseHeader == WorkContext->RequestHeader );
SmbPutUshort( &response->SequenceNumber, 1 );
//
// Set the bit in the SMB that indicates this is a response from the
// server.
//
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
//
// Send the echo. Notice that the smb statistics will be updated
// here. Instead of measuring the time to finish all the echos,
// we just measure the time to respond to the first. This will
// save us the trouble of storing the timestamp somewhere.
//
SRV_START_SEND_2(
WorkContext,
SrvQueueWorkToFspAtSendCompletion,
NULL,
RestartEcho
);
//
// The echo has been started. Tell the main SMB processor not to
// do anything more with the current SMB.
//
SmbStatus = SmbStatusInProgress;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbEcho
VOID SRVFASTCALL
RestartEcho (
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes send completion for an Echo. If more echoes are required,
it sends the next one.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PCONNECTION connection;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_ECHO;
SrvWmiStartContext(WorkContext);
IF_DEBUG(WORKER1) SrvPrint0( " - RestartEcho\n" );
//
// Get the connection pointer. The connection pointer is a
// referenced pointer. (The endpoint is valid because the
// connection references the endpoint.)
//
connection = WorkContext->Connection;
IF_DEBUG(TRACE2) SrvPrint2( " connection %p, endpoint %p\n",
connection, WorkContext->Endpoint );
//
// If the I/O request failed or was canceled, or if the connection
// is no longer active, clean up. (The connection is marked as
// closing when it is disconnected or when the endpoint is closed.)
//
// !!! If I/O failure, should we drop the connection?
//
if ( WorkContext->Irp->Cancel ||
!NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ||
(GET_BLOCK_STATE(connection) != BlockStateActive) ) {
IF_DEBUG(TRACE2) {
if ( WorkContext->Irp->Cancel ) {
SrvPrint0( " I/O canceled\n" );
} else if ( !NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
SrvPrint1( " I/O failed: %X\n",
WorkContext->Irp->IoStatus.Status );
} else {
SrvPrint0( " Connection no longer active\n" );
}
}
//
// Indicate that SMB processing is complete.
//
SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse );
IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" );
goto Cleanup;
}
//
// The request was successful, and the connection is still active.
// If there are no more echoes to be sent, indicate that SMB
// processing is complete.
//
if ( WorkContext->Parameters.RemainingEchoCount == 0 ) {
SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse );
IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" );
goto Cleanup;
}
--WorkContext->Parameters.RemainingEchoCount;
//
// There are more echoes to be sent. Increment the sequence number
// in the response SMB, and send another echo.
//
SmbPutUshort(
&((PRESP_ECHO)WorkContext->ResponseParameters)->SequenceNumber,
(USHORT)(SmbGetUshort(
&((PRESP_ECHO)WorkContext->ResponseParameters)->SequenceNumber
) + 1)
);
//
// Don't do smb statistics a second time.
//
WorkContext->StartTime = 0;
//
// Send the echo. (Note that the response bit has already been
// set.)
//
SRV_START_SEND_2(
WorkContext,
SrvQueueWorkToFspAtSendCompletion,
NULL,
RestartEcho
);
IF_DEBUG(TRACE2) SrvPrint0( "RestartEcho complete\n" );
Cleanup:
SrvWmiEndContext(WorkContext);
return;
} // RestartEcho
SMB_TRANS_STATUS
SrvSmbQueryFsInformation (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Query FS Information request. This request arrives
in a Transaction2 SMB. Query FS Information corresponds to the
OS/2 DosQFSInfo service.
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;
IO_STATUS_BLOCK ioStatusBlock;
PTRANSACTION transaction;
USHORT informationLevel;
USHORT trans2code;
HANDLE fileHandle;
FILE_FS_SIZE_INFORMATION fsSizeInfo;
PFSALLOCATE fsAllocate;
PFILE_FS_VOLUME_INFORMATION fsVolumeInfo;
ULONG fsVolumeInfoLength;
PFSINFO fsInfo;
ULONG lengthVolumeLabel;
BOOLEAN isUnicode;
PREQ_QUERY_FS_INFORMATION request;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_FS_INFORMATION;
SrvWmiStartContext(WorkContext);
isUnicode = SMB_IS_UNICODE( WorkContext );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(MISC1) {
SrvPrint1( "Query FS Information entered; transaction 0x%p\n",
transaction );
}
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes. Query FS information has no
// response parameters.
//
if ( (transaction->ParameterCount < sizeof(REQ_QUERY_FS_INFORMATION)) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) {
SrvPrint2( "SrvSmbQueryFSInformation: bad parameter byte "
"counts: %ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
SrvLogInvalidSmb( WorkContext );
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
status = STATUS_INVALID_SMB;
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
//
// See if a non-admin user is trying to access information on an Administrative share
//
status = SrvIsAllowedOnAdminShare( WorkContext, WorkContext->TreeConnect->Share );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
trans2code = SmbGetAlignedUshort(transaction->InSetup);
IF_SMB_DEBUG(MISC1) {
SrvPrint1("SrvSmbQueryFSInformation: Trans2 function = %x\n", trans2code);
}
request = (PREQ_QUERY_FS_INFORMATION) transaction->InParameters;
ASSERT( trans2code == TRANS2_QUERY_FS_INFORMATION );
informationLevel = SmbGetUshort( &request->InformationLevel );
//
// *** The share handle is used to get the allocation
// information. This is a "storage channel," and as a
// result could allow people to get information to which
// they are not entitled. For a B2 security rating this may
// need to be changed.
//
status = SrvGetShareRootHandle( WorkContext->TreeConnect->Share );
if (!NT_SUCCESS(status)) {
IF_DEBUG(ERRORS) {
SrvPrint1( "SrvSmbQueryFsInformation: SrvGetShareRootHandle failed %x.\n",
status );
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( !NT_SUCCESS(status)) {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
IF_SMB_DEBUG(MISC1) {
SrvPrint0("SrvSmbQueryFSInformation: Using share root handle\n");
}
if( informationLevel < SMB_INFO_PASSTHROUGH ) {
switch ( informationLevel ) {
case SMB_INFO_ALLOCATION:
//
// Return information about the disk.
//
fsAllocate = (PFSALLOCATE)transaction->OutData;
if ( transaction->MaxDataCount < sizeof(FSALLOCATE) ) {
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW );
status = STATUS_BUFFER_OVERFLOW;
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
//
// *** The share handle is used to get the allocation
// information. This is a "storage channel," and as a
// result could allow people to get information to which
// they are not entitled. For a B2 security rating this may
// need to be changed.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
}
}
REVERT();
}
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) {
INTERNAL_ERROR(
ERROR_LEVEL_UNEXPECTED,
"SrvSmbQueryFsInformation: NtQueryVolumeInformationFile "
"returned %X",
status,
NULL
);
SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
SmbPutAlignedUlong( &fsAllocate->idFileSystem, 0 );
SmbPutAlignedUlong(
&fsAllocate->cSectorUnit,
fsSizeInfo.SectorsPerAllocationUnit
);
//
// *** If .HighPart is non-zero, there is a problem, as we can
// only return 32 bits for the volume size. In this case,
// we return the largest value that will fit.
//
SmbPutAlignedUlong(
&fsAllocate->cUnit,
fsSizeInfo.TotalAllocationUnits.HighPart == 0 ?
fsSizeInfo.TotalAllocationUnits.LowPart :
0xffffffff
);
SmbPutAlignedUlong(
&fsAllocate->cUnitAvail,
fsSizeInfo.AvailableAllocationUnits.HighPart == 0 ?
fsSizeInfo.AvailableAllocationUnits.LowPart :
0xffffffff
);
SmbPutAlignedUshort(
&fsAllocate->cbSector,
(USHORT)fsSizeInfo.BytesPerSector );
transaction->DataCount = sizeof(FSALLOCATE);
break;
case SMB_INFO_VOLUME:
//
// Query the volume label.
//
fsInfo = (PFSINFO)transaction->OutData;
//
// The maximum volume label length we are able to return, given
// the VOLUMELABEL structure (1 byte describes length of label),
// is 255 characters. Therefore, allocate a buffer large enough
// to hold a label that size, and if the label is longer then we
// will get STATUS_BUFFER_OVERFLOW from NtQueryVolumeInformationFile.
//
fsVolumeInfoLength = FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel ) +
255 * sizeof(WCHAR);
fsVolumeInfo = ALLOCATE_HEAP_COLD( fsVolumeInfoLength, BlockTypeDataBuffer );
if ( fsVolumeInfo == NULL ) {
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
status = STATUS_INSUFF_SERVER_RESOURCES;
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
//
// Get the label information.
//
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
fsVolumeInfo,
fsVolumeInfoLength,
FileFsVolumeInformation
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
fsVolumeInfo,
fsVolumeInfoLength,
FileFsVolumeInformation
);
}
}
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( !NT_SUCCESS(status) ) {
INTERNAL_ERROR(
ERROR_LEVEL_UNEXPECTED,
"SrvSmbQueryFSInformation: NtQueryVolumeInformationFile "
"returned %X",
status,
NULL
);
FREE_HEAP( fsVolumeInfo );
SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
lengthVolumeLabel = fsVolumeInfo->VolumeLabelLength;
//
// Make sure that the client can accept enough data. The volume
// label length is limited to 13 characters (8 + '.' + 3 + zero
// terminator) in OS/2, so return STATUS_BUFFER_OVERFLOW if the
// label is too long.
//
if ( !isUnicode &&
!IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {
//
// For a non-NT client, we truncate the volume label in case
// it is longer than 11+1 characters.
//
if ( lengthVolumeLabel > 11 * sizeof(WCHAR) ) {
lengthVolumeLabel = 11 * sizeof(WCHAR);
}
//
// Wedge a '.' into the name if it's longer than 8 characters long
//
if( lengthVolumeLabel > 8 * sizeof( WCHAR ) ) {
LPWSTR p = &fsVolumeInfo->VolumeLabel[11];
*p = *(p-1); // VolumeLabel[11] = VolumeLabel[10]
--p;
*p = *(p-1); // VolumeLabel[10] = VolumeLabel[9]
--p;
*p = *(p-1); // VolumeLabel[9] = VolumeLabel[8]
--p;
*p = L'.'; // VolumeLabel[8] = '.'
}
}
if ( (ULONG)transaction->MaxDataCount <
( sizeof(FSINFO) - sizeof(VOLUMELABEL) + sizeof( UCHAR ) +
lengthVolumeLabel / (isUnicode ? 1 : sizeof(WCHAR)) ) ) {
FREE_HEAP( fsVolumeInfo );
SrvSetSmbError( WorkContext, STATUS_BUFFER_OVERFLOW );
status = STATUS_BUFFER_OVERFLOW;
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
SmbPutUlong( &fsInfo->ulVsn, fsVolumeInfo->VolumeSerialNumber );
//
// Put the label in the SMB in Unicode or OEM, depending on what
// was negotiated.
//
if ( isUnicode ) {
RtlCopyMemory(
fsInfo->vol.szVolLabel,
fsVolumeInfo->VolumeLabel,
lengthVolumeLabel
);
transaction->DataCount = sizeof(FSINFO) -
sizeof(VOLUMELABEL) + lengthVolumeLabel;
fsInfo->vol.cch = (UCHAR)lengthVolumeLabel;
} else {
ULONG i;
OEM_STRING oemString;
UNICODE_STRING unicodeString;
if ( lengthVolumeLabel != 0 ) {
oemString.Buffer = fsInfo->vol.szVolLabel;
oemString.MaximumLength = 12;
unicodeString.Buffer = (PWCH)fsVolumeInfo->VolumeLabel;
unicodeString.Length = (USHORT) lengthVolumeLabel;
unicodeString.MaximumLength = (USHORT) lengthVolumeLabel;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
fsInfo->vol.cch = (UCHAR) (lengthVolumeLabel / sizeof(WCHAR));
//
// Pad the end of the volume name with zeros to fill 12
// characters.
//
for ( i = fsInfo->vol.cch + 1 ; i < 12; i++ ) {
fsInfo->vol.szVolLabel[i] = '\0';
}
transaction->DataCount = sizeof(FSINFO);
}
IF_SMB_DEBUG(MISC1) {
SrvPrint2( "volume label length is %d and label is %s\n",
fsInfo->vol.cch, fsInfo->vol.szVolLabel );
}
FREE_HEAP( fsVolumeInfo );
break;
case SMB_QUERY_FS_VOLUME_INFO:
case SMB_QUERY_FS_DEVICE_INFO:
case SMB_QUERY_FS_ATTRIBUTE_INFO:
//
// These are NT infolevels. We always return unicode.
// Except for the fact that NEXUS on WFW calls through here and is
// not unicode (isaache)
//
// ASSERT( isUnicode );
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
MAP_SMB_INFO_TYPE_TO_NT(
QueryVolumeInformation,
informationLevel
)
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
MAP_SMB_INFO_TYPE_TO_NT(
QueryVolumeInformation,
informationLevel
)
);
}
}
REVERT();
}
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) {
//
// We need to return FAT to the client if the host volume is really
// FAT32
//
if( informationLevel == SMB_QUERY_FS_ATTRIBUTE_INFO &&
ioStatusBlock.Information > sizeof( FILE_FS_ATTRIBUTE_INFORMATION ) ) {
PFILE_FS_ATTRIBUTE_INFORMATION attrInfo =
(PFILE_FS_ATTRIBUTE_INFORMATION)(transaction->OutData);
if( attrInfo->FileSystemNameLength > 3*sizeof(WCHAR) &&
attrInfo->FileSystemName[0] == L'F' &&
attrInfo->FileSystemName[1] == L'A' &&
attrInfo->FileSystemName[2] == L'T' ) {
ioStatusBlock.Information =
ioStatusBlock.Information -
(attrInfo->FileSystemNameLength - 3*sizeof(WCHAR) );
attrInfo->FileSystemNameLength = 3 * sizeof(WCHAR);
attrInfo->FileSystemName[3] = UNICODE_NULL;
}
}
transaction->DataCount = (ULONG)ioStatusBlock.Information;
} else {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
break;
case SMB_QUERY_FS_SIZE_INFO:
//
// These are NT infolevels. We always return unicode.
// Except for the fact that NEXUS on WFW calls through here and is
// not unicode (isaache)
//
// ASSERT( isUnicode );
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
MAP_SMB_INFO_TYPE_TO_NT(
QueryVolumeInformation,
informationLevel
)
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
MAP_SMB_INFO_TYPE_TO_NT(
QueryVolumeInformation,
informationLevel
)
);
}
}
REVERT();
}
//
// Release the share root handle
//
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) {
transaction->DataCount = (ULONG)ioStatusBlock.Information;
} else {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
break;
default:
//
// An invalid information level was passed.
//
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
status = STATUS_OS2_INVALID_LEVEL;
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
} else {
informationLevel -= SMB_INFO_PASSTHROUGH;
status = IoCheckQuerySetVolumeInformation( informationLevel,
transaction->MaxDataCount,
FALSE
);
if( NT_SUCCESS( status ) ) {
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
informationLevel
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &fileHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
fileHandle,
&ioStatusBlock,
transaction->OutData,
transaction->MaxDataCount,
informationLevel
);
}
}
REVERT();
}
}
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
if ( NT_SUCCESS( status ) ) {
transaction->DataCount = (ULONG)ioStatusBlock.Information;
} else {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbTransStatusErrorWithoutData;
goto Cleanup;
}
}
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
SmbStatus = SmbTransStatusSuccess;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbQueryFsInformation
SMB_TRANS_STATUS
SrvSmbSetFsInformation (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Set FS Information 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.
--*/
{
SMB_TRANS_STATUS transactionStatus = SmbTransStatusInProgress;
PREQ_SET_FS_INFORMATION request;
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK ioStatusBlock;
PTRANSACTION transaction;
USHORT informationLevel;
PSESSION session;
PTREE_CONNECT treeConnect;
PRFCB rfcb;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_FS_INFORMATION;
SrvWmiStartContext(WorkContext);
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(MISC1) {
SrvPrint1( "Set FS Information entered; transaction 0x%p\n",
transaction );
}
status = SrvVerifyUidAndTid(
WorkContext,
&session,
&treeConnect,
ShareTypeDisk
);
if( !NT_SUCCESS( status ) ) {
goto out;
}
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes. Set FS information has no
// response parameters.
//
request = (PREQ_SET_FS_INFORMATION)transaction->InParameters;
if ( (transaction->ParameterCount < sizeof(REQ_SET_FS_INFORMATION)) ) {
//
// Not enough parameter bytes were sent.
//
IF_SMB_DEBUG(ERRORS) {
SrvPrint2( "SrvSmbSetFSInformation: bad parameter byte "
"counts: %ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
status = STATUS_INVALID_SMB;
SrvLogInvalidSmb( WorkContext );
goto out;
}
//
// Confirm that the information level is legitimate.
//
informationLevel = SmbGetUshort( &request->InformationLevel );
if( informationLevel < SMB_INFO_PASSTHROUGH ) {
status = STATUS_NOT_SUPPORTED;
goto out;
}
informationLevel -= SMB_INFO_PASSTHROUGH;
//
// Make sure the client is allowed to do this, if we have an Admin share
//
status = SrvIsAllowedOnAdminShare( WorkContext, WorkContext->TreeConnect->Share );
if( !NT_SUCCESS( status ) ) {
goto out;
}
//
// 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,
SmbGetUshort( &request->Fid ),
TRUE,
NULL, // don't serialize with raw write
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
IF_DEBUG(ERRORS) {
SrvPrint2(
"SrvSmbSetFsInformation: Status %X on FID: 0x%lx\n",
status,
SmbGetUshort( &request->Fid )
);
}
goto out;
}
status = IoCheckQuerySetVolumeInformation(
informationLevel,
transaction->DataCount,
TRUE
);
if( NT_SUCCESS( status ) ) {
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtSetVolumeInformationFile(
rfcb->Lfcb->FileHandle,
&ioStatusBlock,
transaction->InData,
transaction->DataCount,
informationLevel
);
REVERT();
}
}
out:
if ( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
transactionStatus = SmbTransStatusErrorWithoutData;
} else {
transactionStatus = SmbTransStatusSuccess;
}
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
transaction->DataCount = 0;
SrvWmiEndContext(WorkContext);
return transactionStatus;
} // SrvSmbSetFsInformation
SMB_PROCESSOR_RETURN_TYPE
SrvSmbQueryInformationDisk (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
This routine processes the Query Information Disk 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_QUERY_INFORMATION_DISK request;
PRESP_QUERY_INFORMATION_DISK response;
NTSTATUS status = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
IO_STATUS_BLOCK ioStatusBlock;
FILE_FS_SIZE_INFORMATION fsSizeInfo;
PSESSION session;
PTREE_CONNECT treeConnect;
USHORT totalUnits, freeUnits;
ULONG sectorsPerUnit, bytesPerSector;
LARGE_INTEGER result;
BOOLEAN highpart;
ULONG searchword;
CCHAR highbit, extrabits;
BOOLEAN isDos;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_INFORMATION_DISK;
SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(MISC1) {
SrvPrint2( "Query Information Disk request header at 0x%p, response header at 0x%p\n",
WorkContext->RequestHeader,
WorkContext->ResponseHeader );
SrvPrint2( "Query Information Disk request params at 0x%p, response params%p\n",
WorkContext->RequestParameters,
WorkContext->ResponseParameters );
}
request = (PREQ_QUERY_INFORMATION_DISK)WorkContext->RequestParameters;
response = (PRESP_QUERY_INFORMATION_DISK)WorkContext->ResponseParameters;
//
// 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.
//
// Find tree connect corresponding to given TID if a tree connect
// pointer has not already been put in the WorkContext block by an
// AndX command.
//
status = SrvVerifyUidAndTid(
WorkContext,
&session,
&treeConnect,
ShareTypeDisk
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint0( "SrvSmbQueryInformationDisk: 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;
}
//
// Make sure the client is allowed to do this, if we have an Admin share
//
status = SrvIsAllowedOnAdminShare( WorkContext, treeConnect->Share );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Get the Share root handle.
//
status = SrvGetShareRootHandle( treeConnect->Share );
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "SrvSmbQueryInformationDisk: SrvGetShareRootHandle failed %x.\n",
status );
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// *** The share handle is used to get the allocation information.
// This is a "storage channel," and as a result could allow
// people to get information to which they are not entitled.
// For a B2 security rating this may need to be changed.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
HANDLE RootHandle;
status = SrvSnapGetRootHandle( WorkContext, &RootHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
RootHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
//
// If the media was changed and we can come up with a new share root handle,
// then we should retry the operation
//
if( SrvRetryDueToDismount( WorkContext->TreeConnect->Share, status ) ) {
status = SrvSnapGetRootHandle( WorkContext, &RootHandle );
if( NT_SUCCESS(status) )
{
status = NtQueryVolumeInformationFile(
RootHandle,
&ioStatusBlock,
&fsSizeInfo,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation
);
}
}
}
REVERT();
}
//
// Release the share root handle
//
SrvReleaseShareRootHandle( treeConnect->Share );
if ( !NT_SUCCESS(status) ) {
INTERNAL_ERROR(
ERROR_LEVEL_UNEXPECTED,
"SrvSmbQueryInformationDisk: NtQueryVolumeInformationFile"
"returned %X",
status,
NULL
);
SrvLogServiceFailure( SRV_SVC_NT_SET_VOL_INFO_FILE, status );
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// *** Problem.
//
// This SMB only return 16 bits of information for each field, but we
// may need to return large numbers. In particular TotalAllocationUnits
// is commonly > 64K.
//
// Fortunately, it turns out the all the client cares about is the total
// disk size, in bytes, and the free space, in bytes. So - if one number
// is too big adjust it and adjust the other numbers so that the totals
// come out the same.
//
// If after all adjustment, the number are still too high, return the
// largest possible value for TotalUnit or FreeUnits (i.e. 0xFFFF).
//
// A caveat here is that some DOS apps (like the command interpreter!)
// assume that the cluster size (bytes per sector times sectors per
// cluster) will fit in 16 bits, and will calculate bogus geometry if
// it doesn't. So the first thing we do is ensure that the real
// cluster size is less than 0x10000, if the client is a DOS client.
// This may make the TotalUnits or FreeUnits counts too big, so we'll
// have to round them down, but that's life.
//
// Since we use shifts to adjust the numbers it is possible to lose
// 1 bits when we shift a number to the right. We don't care, we're
// doing our best to fix a broken protocol. NT clients will use
// QueryFSAttribute and will get the correct answer.
//
//
// If this is a DOS client, make the cluster size < 0x10000.
//
isDos = IS_DOS_DIALECT( WorkContext->Connection->SmbDialect );
sectorsPerUnit = fsSizeInfo.SectorsPerAllocationUnit;
bytesPerSector = fsSizeInfo.BytesPerSector;
if ( isDos ) {
while ( (sectorsPerUnit * bytesPerSector) > 0xFFFF ) {
if ( sectorsPerUnit >= 2 ) {
sectorsPerUnit /= 2;
} else {
bytesPerSector /= 2;
}
fsSizeInfo.TotalAllocationUnits.QuadPart *= 2;
fsSizeInfo.AvailableAllocationUnits.QuadPart *= 2;
}
}
//
// Calculate how much the total cluster count needs to be shifted in
// order to fit in a word.
//
if ( fsSizeInfo.TotalAllocationUnits.HighPart != 0 ) {
highpart = TRUE;
searchword = fsSizeInfo.TotalAllocationUnits.HighPart;
} else {
highpart = FALSE;
searchword = fsSizeInfo.TotalAllocationUnits.LowPart;
}
highbit = 0;
while ( searchword != 0 ) {
highbit++;
searchword /= 2;
}
if ( highpart ) {
highbit += 32;
} else {
if ( highbit < 16) {
highbit = 0;
} else {
highbit -= 16;
}
}
if ( highbit > 0 ) {
//
// Attempt to adjust the other values to absorb the excess bits.
// If this is a DOS client, don't let the cluster size get
// bigger than 0xFFFF.
//
extrabits = highbit;
if ( isDos ) {
while ( (highbit > 0) &&
((sectorsPerUnit*bytesPerSector) < 0x8000) ) {
sectorsPerUnit *= 2;
highbit--;
}
} else {
while ( (highbit > 0) && (sectorsPerUnit < 0x8000) ) {
sectorsPerUnit *= 2;
highbit--;
}
while ( (highbit > 0) && (bytesPerSector < 0x8000) ) {
bytesPerSector *= 2;
highbit--;
}
}
//
// Adjust the total and free unit counts.
//
if ( highbit > 0 ) {
//
// There is no way to get the information to fit. Use the
// maximum possible value.
//
totalUnits = 0xFFFF;
} else {
result.QuadPart = fsSizeInfo.TotalAllocationUnits.QuadPart >> extrabits;
ASSERT( result.HighPart == 0 );
ASSERT( result.LowPart < 0x10000 );
totalUnits = (USHORT)result.LowPart;
}
result.QuadPart = fsSizeInfo.AvailableAllocationUnits.QuadPart >>
(CCHAR)(extrabits - highbit);
if ( result.HighPart != 0 || result.LowPart > 0xFFFF ) {
freeUnits = 0xFFFF;
} else {
freeUnits = (USHORT)result.LowPart;
}
} else {
totalUnits = (USHORT)fsSizeInfo.TotalAllocationUnits.LowPart;
freeUnits = (USHORT)fsSizeInfo.AvailableAllocationUnits.LowPart;
}
//
// Build the response SMB.
//
response->WordCount = 5;
SmbPutUshort( &response->TotalUnits, totalUnits );
SmbPutUshort( &response->BlocksPerUnit, (USHORT)sectorsPerUnit );
SmbPutUshort( &response->BlockSize, (USHORT)bytesPerSector );
SmbPutUshort( &response->FreeUnits, freeUnits );
SmbPutUshort( &response->Reserved, 0 );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_QUERY_INFORMATION_DISK,
0
);
SmbStatus = SmbStatusSendResponse;
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbQueryInformationDisk complete.\n" );
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbQueryInformationDisk
SMB_PROCESSOR_RETURN_TYPE
SrvSmbNtCancel (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes an Nt Cancel 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 = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PSESSION session;
PTREE_CONNECT treeConnect;
PCONNECTION connection;
USHORT targetUid, targetPid, targetTid, targetMid;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
PWORK_CONTEXT workContext;
PSMB_HEADER header;
BOOLEAN match;
KIRQL oldIrql;
PREQ_NT_CANCEL request;
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_NT_CANCEL;
SrvWmiStartContext(WorkContext);
request = (PREQ_NT_CANCEL)WorkContext->RequestParameters;
//
// The word count has already been checked. Now make sure that
// the byte count is zero.
//
if ( SmbGetUshort( &request->ByteCount) != 0 ) {
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.
//
// Find tree connect corresponding to given TID if a tree connect
// pointer has not already been put in the WorkContext block by an
// AndX command.
//
status = SrvVerifyUidAndTid(
WorkContext,
&session,
&treeConnect,
ShareTypeWild
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint0( "SrvSmbNtCancel: Invalid UID or TID\n" );
}
SrvSetSmbError( WorkContext, status );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Check the work in-progress list to see if this work item is
// cancellable.
//
targetUid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid );
targetPid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
targetTid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid );
targetMid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Mid );
match = FALSE;
connection = WorkContext->Connection;
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
listHead = &connection->InProgressWorkItemList;
listEntry = listHead;
while ( listEntry->Flink != listHead ) {
listEntry = listEntry->Flink;
workContext = CONTAINING_RECORD(
listEntry,
WORK_CONTEXT,
InProgressListEntry
);
header = workContext->RequestHeader;
//
// Some workitems in the inprogressworkitemlist are added
// during a receive indication and the requestheader field
// has not been set yet. We can probably set it at that time
// but this seems to be the safest fix.
//
// We have to check whether the workitem ref count is zero or
// not since we dereference it before removing it from the
// InProgressWorkItemList queue. This prevents the workitem
// from being cleaned up twice.
//
// We also need to check the processing count of the workitem.
// Work items being used for actual smb requests will have
// a processing count of at least 1. This will prevent us
// from touching oplock breaks and pending tdi receives.
//
ACQUIRE_DPC_SPIN_LOCK( &workContext->SpinLock );
if ( (workContext->BlockHeader.ReferenceCount != 0) &&
(workContext->ProcessingCount != 0) &&
header != NULL &&
header->Command != SMB_COM_NT_CANCEL &&
SmbGetAlignedUshort( &header->Mid ) == targetMid &&
SmbGetAlignedUshort( &header->Pid ) == targetPid &&
SmbGetAlignedUshort( &header->Tid ) == targetTid &&
SmbGetAlignedUshort( &header->Uid ) == targetUid ) {
match = TRUE;
break;
}
RELEASE_DPC_SPIN_LOCK( &workContext->SpinLock );
}
if ( match ) {
//
// Reference the work item, so that it cannot get used to process
// a new SMB while we are trying to cancel the old one.
//
SrvReferenceWorkItem( workContext );
RELEASE_DPC_SPIN_LOCK( &workContext->SpinLock );
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
(VOID)IoCancelIrp( workContext->Irp );
SrvDereferenceWorkItem( workContext );
} else {
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
}
//
// Done. Do not send a response
//
SmbStatus = SmbStatusNoResponse;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbNtCancel
SMB_TRANS_STATUS
SrvSmbSetSecurityDescriptor (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Set Security Descriptor 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.
--*/
{
PREQ_SET_SECURITY_DESCRIPTOR request;
NTSTATUS status;
PTRANSACTION transaction;
PRFCB rfcb;
SECURITY_INFORMATION securityInformation;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(QUERY_SET1) {
SrvPrint1( "Set Security Descriptor entered; transaction 0x%p\n",
transaction );
}
request = (PREQ_SET_SECURITY_DESCRIPTOR)transaction->InParameters;
//
// Verify that enough setup bytes were sent.
//
if ( transaction->ParameterCount < sizeof(REQ_SET_SECURITY_DESCRIPTOR ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "SrvSmbSetSecurityInformation: bad setup byte count: "
"%ld\n",
transaction->ParameterCount );
}
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// 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,
SmbGetUshort( &request->Fid ),
TRUE,
NULL, // don't serialize with raw write
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
IF_DEBUG(ERRORS) {
SrvPrint2(
"SrvSmbSetFileInformation: Status %X on FID: 0x%lx\n",
status,
SmbGetUshort( &request->Fid )
);
}
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// First we'll validate that the security descriptor isn't bogus.
// This needs to be done here because NtSetSecurityObject has no
// idea what the buffer size is.
//
if( !RtlValidRelativeSecurityDescriptor( transaction->InData,
transaction->DataCount,
0 )) {
//
// We were passed a bogus security descriptor to set. Bounce the
// request as an invalid SMB.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SECURITY_DESCR );
return SmbTransStatusErrorWithoutData;
}
securityInformation = SmbGetUlong( &request->SecurityInformation );
//
// Make sure the caller is allowed to set security information on this object
//
status = IoCheckFunctionAccess( rfcb->GrantedAccess,
IRP_MJ_SET_SECURITY,
0,
0,
&securityInformation,
NULL
);
if( NT_SUCCESS( status ) ) {
//
// Attempt to set the security descriptor. We need to be in the
// the user context to do this, in case the security information
// specifies change ownership.
//
status = IMPERSONATE( WorkContext );
if( NT_SUCCESS( status ) ) {
status = NtSetSecurityObject(
rfcb->Lfcb->FileHandle,
securityInformation,
transaction->InData
);
REVERT();
}
}
//
// If an error occurred, return an appropriate response.
//
if ( !NT_SUCCESS(status) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// We probably shouldn't cache this file descriptor on close, since
// the security setting changed.
//
rfcb->IsCacheable = FALSE;
transaction->ParameterCount = 0;
transaction->DataCount = 0;
return SmbTransStatusSuccess;
} // SrvSmbSetSecurityDescriptor
SMB_TRANS_STATUS
SrvSmbQuerySecurityDescriptor (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Query Security Descriptor 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.
--*/
{
PREQ_QUERY_SECURITY_DESCRIPTOR request;
PRESP_QUERY_SECURITY_DESCRIPTOR response;
NTSTATUS status;
PTRANSACTION transaction;
PRFCB rfcb;
ULONG lengthNeeded;
SECURITY_INFORMATION securityInformation;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(QUERY_SET1) {
SrvPrint1( "Query Security Descriptor entered; transaction 0x%p\n",
transaction );
}
request = (PREQ_QUERY_SECURITY_DESCRIPTOR)transaction->InParameters;
response = (PRESP_QUERY_SECURITY_DESCRIPTOR)transaction->OutParameters;
//
// Verify that enough setup bytes were sent.
//
if ( transaction->ParameterCount < sizeof(REQ_QUERY_SECURITY_DESCRIPTOR ) ||
transaction->MaxParameterCount <
sizeof( RESP_QUERY_SECURITY_DESCRIPTOR ) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) {
SrvPrint2( "SrvSmbQuerySecurityInformation: bad parameter byte or "
"return parameter count: %ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// 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,
SmbGetUshort( &request->Fid ),
TRUE,
NULL, // don't serialize with raw write
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
IF_DEBUG(ERRORS) {
SrvPrint2(
"SrvSmbSetFileInformation: Status %X on FID: 0x%lx\n",
status,
SmbGetUshort( &request->Fid )
);
}
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
securityInformation = SmbGetUlong( &request->SecurityInformation ),
//
// Make sure the caller is allowed to query security information on this object
//
status = IoCheckFunctionAccess( rfcb->GrantedAccess,
IRP_MJ_QUERY_SECURITY,
0,
0,
&securityInformation,
NULL
);
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// Attempt to query the security descriptor
//
status = NtQuerySecurityObject(
rfcb->Lfcb->FileHandle,
securityInformation,
transaction->OutData,
transaction->MaxDataCount,
&lengthNeeded
);
SmbPutUlong( &response->LengthNeeded, lengthNeeded );
transaction->ParameterCount = sizeof( RESP_QUERY_SECURITY_DESCRIPTOR );
//
// If an error occurred, return an appropriate response.
//
if ( !NT_SUCCESS(status) ) {
transaction->DataCount = 0;
SrvSetSmbError2( WorkContext, status, TRUE );
return SmbTransStatusErrorWithData;
} else {
transaction->DataCount =
RtlLengthSecurityDescriptor( transaction->OutData );
}
return SmbTransStatusSuccess;
} // SrvSmbQuerySecurityDescriptor
SMB_TRANS_STATUS
SrvSmbQueryQuota (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes an NtQueryQuotaInformationFile request. This request arrives in an
Nt Transaction SMB.
--*/
{
PREQ_NT_QUERY_FS_QUOTA_INFO request;
PRESP_NT_QUERY_FS_QUOTA_INFO response;
NTSTATUS status;
PTRANSACTION transaction;
PRFCB rfcb;
PVOID sidList;
ULONG sidListLength,startSidLength,startSidOffset;
PVOID sidListBuffer = NULL;
PULONG startSid = NULL;
ULONG errorOffset;
IO_STATUS_BLOCK iosb;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_NT_QUERY_FS_QUOTA_INFO)transaction->InParameters;
response = (PRESP_NT_QUERY_FS_QUOTA_INFO)transaction->OutParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( transaction->ParameterCount < sizeof( *request ) ||
transaction->MaxParameterCount < sizeof( *response ) ) {
//
// Not enough parameter bytes were sent.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// 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,
SmbGetUshort( &request->Fid ),
TRUE,
NULL, // don't serialize with raw write
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
sidListLength = SmbGetUlong( &request->SidListLength );
startSidLength = SmbGetUlong( &request->StartSidLength );
startSidOffset = SmbGetUlong( &request->StartSidOffset );
//
// If a Sid List is supplied, make sure it is OK
//
if( sidListLength != 0 ) {
//
// Length OK?
//
if( sidListLength > transaction->DataCount ) {
SrvSetSmbError( WorkContext, STATUS_INVALID_SID );
return SmbTransStatusErrorWithoutData;
}
sidListBuffer = transaction->InData;
//
// Alignment OK?
//
if( (ULONG_PTR)sidListBuffer & (sizeof(ULONG)-1) ) {
SrvSetSmbError( WorkContext, STATUS_INVALID_SID );
return SmbTransStatusErrorWithoutData;
}
//
// Content OK?
//
#if XXX
status = IopCheckGetQuotaBufferValidity( sidListBuffer, sidListLength, errorOffset );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
#endif
}
// The way the transaction buffers are setup the same buffer pointer is used
// for the incoming data and the outgoing data. This will not work for
// NtQueryQuotaInformationFile since the underlying driver zeroes the
// output buffer before processing the input buffer. This presents us with
// two options ... (1) we can adjust the copying to be staggerred assuming
// that we can contain both the buffers into the transaction buffer or (2)
// allocate anew buffer before calling the QueryQuotaInformationFile.
// The second approach has been implemented since it is well contained.
// If this turns out to be a performance problem we will revert back to the
// first option.
if (sidListLength + startSidLength > 0 &&
startSidOffset <= transaction->DataCount &&
startSidLength <= transaction->DataCount &&
startSidOffset >= sidListLength &&
startSidOffset + startSidLength <= transaction->DataCount ) {
sidListBuffer = ALLOCATE_HEAP( startSidOffset + startSidLength, BlockTypeMisc );
if (sidListBuffer != NULL) {
RtlCopyMemory(
sidListBuffer,
transaction->InData,
sidListLength);
if (startSidLength != 0) {
startSid = (PULONG)((PBYTE)sidListBuffer + startSidOffset);
RtlCopyMemory(
startSid,
((PBYTE)transaction->InData + startSidOffset),
startSidLength);
}
}
} else {
sidListBuffer = NULL;
}
iosb.Information = 0;
//
// Go ahead and query the quota information!
//
status = NtQueryQuotaInformationFile(
rfcb->Lfcb->FileHandle,
&iosb,
transaction->OutData,
transaction->MaxDataCount,
request->ReturnSingleEntry,
sidListBuffer,
sidListLength,
startSid,
request->RestartScan
);
if (sidListBuffer != NULL) {
FREE_HEAP(sidListBuffer);
}
//
// Paranoia
//
if( iosb.Information > transaction->MaxDataCount ) {
iosb.Information = transaction->MaxDataCount;
}
transaction->SetupCount = 0;
SmbPutUlong( &response->Length, (ULONG)iosb.Information );
transaction->ParameterCount = sizeof( *response );
transaction->DataCount = (ULONG)iosb.Information;
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError2( WorkContext, status, TRUE );
return SmbTransStatusErrorWithData;
}
return SmbTransStatusSuccess;
} // SrvSmbQueryQuota
SMB_TRANS_STATUS
SrvSmbSetQuota (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes an NtSetQuotaInformationFile request. This request arrives in an
Nt Transaction SMB.
--*/
{
PREQ_NT_SET_FS_QUOTA_INFO request;
NTSTATUS status;
PTRANSACTION transaction;
PRFCB rfcb;
PVOID buffer,pQuotaInfo=NULL;
ULONG errorOffset;
IO_STATUS_BLOCK iosb;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_NT_SET_FS_QUOTA_INFO)transaction->InParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( transaction->ParameterCount < sizeof( *request ) ) {
//
// Not enough parameter bytes were sent.
//
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// 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,
SmbGetUshort( &request->Fid ),
TRUE,
NULL, // don't serialize with raw write
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
//
// Invalid file ID or write behind error. Reject the request.
//
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// We do not need to check the buffer for validity, because
// IopSetEaOrQuotaInformationFile does this even for kernel mode callers!
//
iosb.Information = 0;
// we have to do allocation here in order to get a QUAD_WORD
// aligned pointer. This is so because this is a requirement on
// alpha for the quota buffer
pQuotaInfo = ALLOCATE_HEAP_COLD( transaction->DataCount, BlockTypeDataBuffer );
if (pQuotaInfo)
{
RtlCopyMemory(
pQuotaInfo,
transaction->InData,
transaction->DataCount
);
//
// Go ahead and set the quota information!
//
status = NtSetQuotaInformationFile(
rfcb->Lfcb->FileHandle,
&iosb,
pQuotaInfo,
transaction->DataCount
);
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
}
//
// Nothing to return to the client except the status
//
transaction->SetupCount = 0;
transaction->ParameterCount = 0;
transaction->DataCount = 0;
FREE_HEAP(pQuotaInfo);
}
else
{
SrvSetSmbError( WorkContext, STATUS_INSUFFICIENT_RESOURCES );
}
return SmbTransStatusSuccess;
}