2351 lines
68 KiB
C
2351 lines
68 KiB
C
/*++
|
||
|
||
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;
|
||
}
|