1165 lines
34 KiB
C
1165 lines
34 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbdir.c
|
||
|
||
Abstract:
|
||
|
||
This module implements directory control SMB processors:
|
||
|
||
Create Directory
|
||
Create Directory2
|
||
Delete Directory
|
||
Check Directory
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "smbdir.tmh"
|
||
#pragma hdrstop
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvSmbCreateDirectory )
|
||
#pragma alloc_text( PAGE, SrvSmbCreateDirectory2 )
|
||
#pragma alloc_text( PAGE, SrvSmbDeleteDirectory )
|
||
#pragma alloc_text( PAGE, SrvSmbCheckDirectory )
|
||
#endif
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbCreateDirectory (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the Create Directory 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_CREATE_DIRECTORY request;
|
||
PRESP_CREATE_DIRECTORY response;
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
UNICODE_STRING directoryName;
|
||
HANDLE directoryHandle;
|
||
|
||
PTREE_CONNECT treeConnect;
|
||
PSESSION session;
|
||
PSHARE share;
|
||
BOOLEAN isUnicode;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_CREATE_DIRECTORY;
|
||
SrvWmiStartContext(WorkContext);
|
||
|
||
IF_SMB_DEBUG(DIRECTORY1) {
|
||
SrvPrint2( "Create directory request header at 0x%p, response header at 0x%p\n",
|
||
WorkContext->RequestHeader,
|
||
WorkContext->ResponseHeader );
|
||
SrvPrint2( "Create directory request params at 0x%p, response params%p\n",
|
||
WorkContext->RequestParameters,
|
||
WorkContext->ResponseParameters );
|
||
}
|
||
|
||
request = (PREQ_CREATE_DIRECTORY)WorkContext->RequestParameters;
|
||
response = (PRESP_CREATE_DIRECTORY)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( "SrvSmbCreateDirectory: Invalid UID or TID\n" );
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( session->IsSessionExpired )
|
||
{
|
||
status = SESSION_EXPIRED_STATUS_CODE;
|
||
SrvSetSmbError( WorkContext, SESSION_EXPIRED_STATUS_CODE );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the share block from the tree connect block. This doesn't need
|
||
// to be a referenced pointer because the tree connect has it referenced,
|
||
// and we just referenced the tree connect.
|
||
//
|
||
|
||
share = treeConnect->Share;
|
||
|
||
//
|
||
// Initialize the string containing the path name. The +1 is to account
|
||
// for the ASCII token in the Buffer field of the request SMB.
|
||
//
|
||
|
||
isUnicode = SMB_IS_UNICODE( WorkContext );
|
||
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
share,
|
||
NULL,
|
||
(PVOID)(request->Buffer + 1),
|
||
END_OF_REQUEST_SMB( WorkContext ),
|
||
TRUE,
|
||
isUnicode,
|
||
&directoryName
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint1( "SrvSmbCreateDirectory: illegal path name: %s\n",
|
||
(PSZ)request->Buffer + 1 );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Initialize the object attributes structure. Open relative to the
|
||
// share root directory handle.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&directoryName,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
WorkContext->Session->UsingUppercasePaths) ?
|
||
OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Attempt to create the directory. Since we must specify some desired
|
||
// access, request TRAVERSE_DIRECTORY even though we are going to close
|
||
// the directory just after we create it. The SMB protocol has no way
|
||
// of specifying attributes, so assume a normal file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "Creating directory %wZ\n", objectAttributes.ObjectName );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&directoryHandle,
|
||
FILE_TRAVERSE, // DesiredAccess
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
0L, // AllocationSize
|
||
FILE_ATTRIBUTE_NORMAL, // FileAttributes
|
||
0L, // ShareAccess
|
||
FILE_CREATE, // Disposition
|
||
FILE_DIRECTORY_FILE, // CreateOptions
|
||
NULL, // EaBuffer
|
||
0L, // EaLength
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
share
|
||
);
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the
|
||
// statistics database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
//
|
||
// Special error mapping to return correct error.
|
||
//
|
||
|
||
if ( status == STATUS_OBJECT_NAME_COLLISION &&
|
||
!CLIENT_CAPABLE_OF(NT_STATUS, WorkContext->Connection)) {
|
||
status = STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvCreateDirectory: SrvIoCreateFile failed, "
|
||
"status = %X\n", status );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( directoryHandle, "DIR", 23, 0 );
|
||
SrvStatistics.TotalFilesOpened++;
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "SrvIoCreateFile succeeded, handle = 0x%p\n",
|
||
directoryHandle );
|
||
}
|
||
|
||
//
|
||
// The SMB protocol has no concept of open directories; just close the
|
||
// handle now that we have created the directory.
|
||
//
|
||
|
||
SRVDBG_RELEASE_HANDLE( directoryHandle, "DIR", 36, 0 );
|
||
SrvNtClose( directoryHandle, TRUE );
|
||
|
||
//
|
||
// Build the response SMB.
|
||
//
|
||
|
||
response->WordCount = 0;
|
||
SmbPutUshort( &response->ByteCount, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_CREATE_DIRECTORY,
|
||
0
|
||
);
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbCreateDirectory complete.\n" );
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatusSendResponse;
|
||
} // SrvSmbCreateDirectory
|
||
|
||
|
||
SMB_TRANS_STATUS
|
||
SrvSmbCreateDirectory2 (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the Create Directory2 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:
|
||
|
||
BOOLEAN - Indicates whether an error occurred. See smbtypes.h for a
|
||
more complete description.
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQ_CREATE_DIRECTORY2 request;
|
||
PRESP_CREATE_DIRECTORY2 response;
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PTRANSACTION transaction;
|
||
UNICODE_STRING directoryName;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE directoryHandle;
|
||
|
||
PFILE_FULL_EA_INFORMATION ntFullEa = NULL;
|
||
ULONG ntFullEaLength = 0;
|
||
USHORT eaErrorOffset = 0;
|
||
|
||
PTREE_CONNECT treeConnect;
|
||
PSHARE share;
|
||
BOOLEAN isUnicode;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_CREATE_DIRECTORY2;
|
||
SrvWmiStartContext(WorkContext);
|
||
|
||
transaction = WorkContext->Parameters.Transaction;
|
||
IF_SMB_DEBUG(DIRECTORY1) {
|
||
SrvPrint1( "Create Directory2 entered; transaction 0x%p\n",
|
||
transaction );
|
||
}
|
||
|
||
request = (PREQ_CREATE_DIRECTORY2)transaction->InParameters;
|
||
response = (PRESP_CREATE_DIRECTORY2)transaction->OutParameters;
|
||
|
||
//
|
||
// Verify that enough parameter bytes were sent and that we're allowed
|
||
// to return enough parameter bytes.
|
||
//
|
||
|
||
if ( (transaction->ParameterCount <
|
||
sizeof(REQ_CREATE_DIRECTORY2)) ||
|
||
(transaction->MaxParameterCount <
|
||
sizeof(RESP_CREATE_DIRECTORY2)) ) {
|
||
|
||
//
|
||
// Not enough parameter bytes were sent.
|
||
//
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint2( "SrvSmbCreateDirectory2: 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;
|
||
}
|
||
|
||
//
|
||
// Get the tree connect block from the transaction block and the share
|
||
// block from the tree connect block. These don't need to be referenced
|
||
// pointers because they are referenced by the transaction and the
|
||
// tree connect, respectively.
|
||
//
|
||
|
||
treeConnect = transaction->TreeConnect;
|
||
share = treeConnect->Share;
|
||
|
||
//
|
||
// Initialize the string containing the path name.
|
||
//
|
||
|
||
isUnicode = SMB_IS_UNICODE( WorkContext );
|
||
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
share,
|
||
NULL,
|
||
request->Buffer,
|
||
END_OF_TRANSACTION_PARAMETERS( transaction ),
|
||
TRUE,
|
||
isUnicode,
|
||
&directoryName
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint1( "SrvSmbCreateDirectory2: illegal path name: %ws\n",
|
||
directoryName.Buffer );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
SmbStatus = SmbTransStatusErrorWithoutData;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Initialize the object attributes structure. Open relative to the
|
||
// share root directory handle.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&directoryName,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
WorkContext->Session->UsingUppercasePaths) ?
|
||
OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// If an FEALIST was passed and it has valid size, convert it to
|
||
// NT style. SrvOs2FeaListToNt allocates space for the NT full EA
|
||
// list which must be deallocated. Note that sizeof(FEALIST) includes
|
||
// space for 1 FEA entry. Without at least much information, the EA
|
||
// code below should be skipped.
|
||
//
|
||
|
||
if ( transaction->DataCount > sizeof(FEALIST) &&
|
||
SmbGetUlong( &((PFEALIST)transaction->InData)->cbList ) > sizeof(FEALIST) &&
|
||
SmbGetUlong( &((PFEALIST)transaction->InData)->cbList ) <= transaction->DataCount ) {
|
||
|
||
status = SrvOs2FeaListToNt(
|
||
(PFEALIST)transaction->InData,
|
||
&ntFullEa,
|
||
&ntFullEaLength,
|
||
&eaErrorOffset
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvSmbCreateDirectory2: SrvOs2FeaListToNt failed, "
|
||
"status = %X\n", status );
|
||
}
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
SrvSetSmbError2( WorkContext, status, TRUE );
|
||
transaction->SetupCount = 0;
|
||
transaction->ParameterCount = 2;
|
||
SmbPutUshort( &response->EaErrorOffset, eaErrorOffset );
|
||
transaction->DataCount = 0;
|
||
|
||
SmbStatus = SmbTransStatusErrorWithData;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Attempt to create the directory. Since we must specify some desired
|
||
// access, request FILE_TRAVERSE even though we are going to close
|
||
// the directory just after we create it. The SMB protocol has no way
|
||
// of specifying attributes, so assume a normal file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "Creating directory %wZ\n", objectAttributes.ObjectName );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
//
|
||
// Ensure the EaBuffer is correctly formatted. Since we are a kernel mode
|
||
// component, the Io subsystem does not check it for us.
|
||
//
|
||
if( ARGUMENT_PRESENT( ntFullEa ) ) {
|
||
ULONG ntEaErrorOffset = 0;
|
||
status = IoCheckEaBufferValidity( ntFullEa, ntFullEaLength, &ntEaErrorOffset );
|
||
eaErrorOffset = (USHORT)ntEaErrorOffset;
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&directoryHandle,
|
||
FILE_TRAVERSE, // DesiredAccess
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
0L, // AllocationSize
|
||
FILE_ATTRIBUTE_NORMAL, // FileAttributes
|
||
0L, // ShareAccess
|
||
FILE_CREATE, // Disposition
|
||
FILE_DIRECTORY_FILE, // CreateOptions
|
||
ntFullEa, // EaBuffer
|
||
ntFullEaLength, // EaLength
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
share
|
||
);
|
||
}
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the statistics
|
||
// database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
if ( ARGUMENT_PRESENT( ntFullEa ) ) {
|
||
DEALLOCATE_NONPAGED_POOL( ntFullEa );
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvCreateDirectory2: SrvIoCreateFile failed, "
|
||
"status = %X\n", status );
|
||
}
|
||
|
||
SrvSetSmbError2( WorkContext, status, TRUE );
|
||
|
||
transaction->SetupCount = 0;
|
||
transaction->ParameterCount = 2;
|
||
SmbPutUshort( &response->EaErrorOffset, eaErrorOffset );
|
||
transaction->DataCount = 0;
|
||
|
||
SmbStatus = SmbTransStatusErrorWithData;
|
||
goto Cleanup;
|
||
}
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "SrvIoCreateFile succeeded, handle = 0x%p\n",
|
||
directoryHandle );
|
||
}
|
||
|
||
//
|
||
// The SMB protocol has no concept of open directories; just close the
|
||
// handle now that we have created the directory.
|
||
//
|
||
|
||
SRVDBG_CLAIM_HANDLE( directoryHandle, "DIR", 24, 0 );
|
||
SRVDBG_RELEASE_HANDLE( directoryHandle, "DIR", 37, 0 );
|
||
SrvNtClose( directoryHandle, TRUE );
|
||
|
||
//
|
||
// Build the output parameter and data structures.
|
||
//
|
||
|
||
transaction->SetupCount = 0;
|
||
transaction->ParameterCount = 2;
|
||
SmbPutUshort( &response->EaErrorOffset, 0 );
|
||
transaction->DataCount = 0;
|
||
SmbStatus = SmbTransStatusSuccess;
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatus;
|
||
|
||
} // SrvSmbCreateDirectory2
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbDeleteDirectory (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the Delete Directory 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_DELETE_DIRECTORY request;
|
||
PRESP_DELETE_DIRECTORY response;
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
UNICODE_STRING directoryName;
|
||
HANDLE directoryHandle;
|
||
FILE_DISPOSITION_INFORMATION fileDispositionInformation;
|
||
|
||
PTREE_CONNECT treeConnect;
|
||
PSESSION session;
|
||
PSHARE share;
|
||
BOOLEAN isUnicode;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_DELETE_DIRECTORY;
|
||
SrvWmiStartContext(WorkContext);
|
||
IF_SMB_DEBUG(DIRECTORY1) {
|
||
SrvPrint2( "Delete directory request header at 0x%p, response header at 0x%p\n",
|
||
WorkContext->RequestHeader,
|
||
WorkContext->ResponseHeader );
|
||
SrvPrint2( "Delete directory request params at 0x%p, response params at 0x%p\n",
|
||
WorkContext->RequestParameters,
|
||
WorkContext->ResponseParameters );
|
||
}
|
||
|
||
request = (PREQ_DELETE_DIRECTORY)WorkContext->RequestParameters;
|
||
response = (PRESP_DELETE_DIRECTORY)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( "SrvSmbDeleteDirectory: Invalid UID or TID\n" );
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( session->IsSessionExpired )
|
||
{
|
||
status = SESSION_EXPIRED_STATUS_CODE;
|
||
SrvSetSmbError( WorkContext, SESSION_EXPIRED_STATUS_CODE );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the share block from the tree connect block. This doesn't need
|
||
// to be a referenced pointer becsue the tree connect has it referenced,
|
||
// and we just referenced the tree connect.
|
||
//
|
||
|
||
share = treeConnect->Share;
|
||
|
||
//
|
||
// Initialize the string containing the path name. The +1 is to account
|
||
// for the ASCII token in the Buffer field of the request SMB.
|
||
//
|
||
|
||
isUnicode = SMB_IS_UNICODE( WorkContext );
|
||
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
share,
|
||
NULL,
|
||
(PVOID)(request->Buffer + 1),
|
||
END_OF_REQUEST_SMB( WorkContext ),
|
||
TRUE,
|
||
isUnicode,
|
||
&directoryName
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint1( "SrvSmbDeleteDirectory: illegal path name: %s\n",
|
||
(PSZ)request->Buffer + 1 );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If the client is trying to delete the root of the share, reject
|
||
// the request.
|
||
//
|
||
|
||
if ( directoryName.Length < sizeof(WCHAR) ) {
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint0( "SrvSmbDeleteDirectory: attempting to delete share root\n" );
|
||
}
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Initialize the object attributes structure. Open relative to the
|
||
// share root directory handle.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&directoryName,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
WorkContext->Session->UsingUppercasePaths) ?
|
||
OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Attempt to open the directory. We just need DELETE access to delete
|
||
// the directory.
|
||
//
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "Opening directory %wZ\n", &directoryName );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&directoryHandle,
|
||
DELETE, // DesiredAccess
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0L, // FileAttributes
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN, // Disposition
|
||
FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT, // CreateOptions
|
||
NULL, // EaBuffer
|
||
0L, // EaLength
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
share
|
||
);
|
||
|
||
if( status == STATUS_INVALID_PARAMETER ) {
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&directoryHandle,
|
||
DELETE, // DesiredAccess
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0L, // FileAttributes
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
FILE_OPEN, // Disposition
|
||
FILE_DIRECTORY_FILE, // CreateOptions
|
||
NULL, // EaBuffer
|
||
0L, // EaLength
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
share
|
||
);
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the
|
||
// statistics database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint2( "SrvDeleteDirectory: SrvIoCreateFile (%s) failed, "
|
||
"status = %X\n", (PSZ)request->Buffer + 1, status );
|
||
}
|
||
|
||
//
|
||
// If returned error is STATUS_NOT_A_DIRECTORY, downlevel clients
|
||
// expect ERROR_ACCESS_DENIED
|
||
//
|
||
|
||
if ( (status == STATUS_NOT_A_DIRECTORY) &&
|
||
!CLIENT_CAPABLE_OF(NT_STATUS, WorkContext->Connection) ) {
|
||
|
||
status = STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( directoryHandle, "DIR", 25, 0 );
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint1( "SrvIoCreateFile succeeded, handle = 0x%p\n",
|
||
directoryHandle );
|
||
}
|
||
|
||
//
|
||
// Delete the directory with NtSetInformationFile.
|
||
//
|
||
|
||
fileDispositionInformation.DeleteFile = TRUE;
|
||
|
||
status = NtSetInformationFile(
|
||
directoryHandle,
|
||
&ioStatusBlock,
|
||
&fileDispositionInformation,
|
||
sizeof(FILE_DISPOSITION_INFORMATION),
|
||
FileDispositionInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint2( "SrvDeleteDirectory: NtSetInformationFile for directory "
|
||
"%s returned %X\n", (PSZ)request->Buffer + 1, status );
|
||
}
|
||
|
||
SRVDBG_RELEASE_HANDLE( directoryHandle, "DIR", 38, 0 );
|
||
SrvNtClose( directoryHandle, TRUE );
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Remove this directory name from the cache, since it has been deleted
|
||
//
|
||
SrvRemoveCachedDirectoryName( WorkContext, &directoryName );
|
||
}
|
||
|
||
IF_SMB_DEBUG(DIRECTORY2) {
|
||
SrvPrint0( "SrvSmbDeleteDirectory: NtSetInformationFile succeeded.\n" );
|
||
}
|
||
|
||
//
|
||
// Close the directory handle so that the directory will be deleted.
|
||
//
|
||
|
||
SRVDBG_RELEASE_HANDLE( directoryHandle, "DIR", 39, 0 );
|
||
SrvNtClose( directoryHandle, TRUE );
|
||
|
||
//
|
||
// Close all DOS directory searches on this directory and its
|
||
// subdirectories.
|
||
//
|
||
|
||
SrvCloseSearches(
|
||
treeConnect->Connection,
|
||
(PSEARCH_FILTER_ROUTINE)SrvSearchOnDelete,
|
||
(PVOID) &directoryName,
|
||
(PVOID) treeConnect
|
||
);
|
||
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
//
|
||
// Build the response SMB.
|
||
//
|
||
|
||
response->WordCount = 0;
|
||
SmbPutUshort( &response->ByteCount, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_DELETE_DIRECTORY,
|
||
0
|
||
);
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbDeleteDirectory complete.\n" );
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatusSendResponse;
|
||
|
||
} // SrvSmbDeleteDirectory
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbCheckDirectory (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the Check Directory 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_CHECK_DIRECTORY request;
|
||
PRESP_CHECK_DIRECTORY response;
|
||
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
UNICODE_STRING directoryName;
|
||
FILE_NETWORK_OPEN_INFORMATION fileInformation;
|
||
PTREE_CONNECT treeConnect;
|
||
PSESSION session;
|
||
PSHARE share;
|
||
BOOLEAN isUnicode;
|
||
|
||
PAGED_CODE( );
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_CHECK_DIRECTORY;
|
||
SrvWmiStartContext(WorkContext);
|
||
IF_SMB_DEBUG(DIRECTORY1) {
|
||
SrvPrint2( "Check directory request header at 0x%p, response header at 0x%p\n",
|
||
WorkContext->RequestHeader,
|
||
WorkContext->ResponseHeader );
|
||
SrvPrint2( "Check directory request params at 0x%p, response params at 0x%p\n",
|
||
WorkContext->RequestParameters,
|
||
WorkContext->ResponseParameters );
|
||
}
|
||
|
||
request = (PREQ_CHECK_DIRECTORY)WorkContext->RequestParameters;
|
||
response = (PRESP_CHECK_DIRECTORY)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( "SrvSmbCheckDirectory: Invalid UID or TID\n" );
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( session->IsSessionExpired )
|
||
{
|
||
status = SESSION_EXPIRED_STATUS_CODE;
|
||
SrvSetSmbError( WorkContext, SESSION_EXPIRED_STATUS_CODE );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the share block from the tree connect block. This doesn't need
|
||
// to be a referenced pointer because the tree connect has it referenced,
|
||
// and we just referenced the tree connect.
|
||
//
|
||
|
||
share = treeConnect->Share;
|
||
|
||
//
|
||
// Initialize the string containing the path name. The +1 is to account
|
||
// for the ASCII token in the Buffer field of the request SMB.
|
||
//
|
||
|
||
isUnicode = SMB_IS_UNICODE( WorkContext );
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
share,
|
||
NULL,
|
||
(PVOID)(request->Buffer + 1),
|
||
END_OF_REQUEST_SMB( WorkContext ),
|
||
TRUE,
|
||
isUnicode,
|
||
&directoryName
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
SrvPrint1( "SrvSmbCheckDirectory: illegal path name: %s\n",
|
||
(PSZ)request->Buffer + 1 );
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// See if we can find this directory in the CachedDirectoryList
|
||
//
|
||
if( SrvIsDirectoryCached( WorkContext, &directoryName ) == FALSE ) {
|
||
|
||
//
|
||
// Is not in the cache, must really check.
|
||
//
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&directoryName,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
WorkContext->Session->UsingUppercasePaths) ?
|
||
OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = IMPERSONATE( WorkContext );
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
status = SrvGetShareRootHandle( share );
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
ULONG FileOptions = FILE_DIRECTORY_FILE;
|
||
|
||
if (SeSinglePrivilegeCheck(
|
||
SeExports->SeBackupPrivilege,
|
||
KernelMode)) {
|
||
FileOptions |= FILE_OPEN_FOR_BACKUP_INTENT;
|
||
}
|
||
|
||
//
|
||
// The file name is always relative to the share root
|
||
//
|
||
status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
|
||
if( !NT_SUCCESS(status) )
|
||
{
|
||
goto SnapError;
|
||
}
|
||
|
||
//
|
||
// Find out what this thing is
|
||
//
|
||
|
||
if( IoFastQueryNetworkAttributes( &objectAttributes,
|
||
FILE_TRAVERSE,
|
||
FileOptions,
|
||
&ioStatusBlock,
|
||
&fileInformation
|
||
) == FALSE ) {
|
||
|
||
SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
|
||
ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
|
||
status = ioStatusBlock.Status;
|
||
|
||
//
|
||
// If the media was changed and we can come up with a new share root handle,
|
||
// then we should retry the operation
|
||
//
|
||
if( SrvRetryDueToDismount( share, status ) ) {
|
||
|
||
status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
|
||
if( !NT_SUCCESS(status) )
|
||
{
|
||
goto SnapError;
|
||
}
|
||
|
||
if( IoFastQueryNetworkAttributes( &objectAttributes,
|
||
FILE_TRAVERSE,
|
||
FILE_DIRECTORY_FILE,
|
||
&ioStatusBlock,
|
||
&fileInformation
|
||
) == FALSE ) {
|
||
|
||
SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
|
||
ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
|
||
SnapError:
|
||
|
||
SrvReleaseShareRootHandle( share );
|
||
}
|
||
|
||
REVERT();
|
||
}
|
||
}
|
||
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &directoryName );
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
|
||
response->WordCount = 0;
|
||
SmbPutUshort( &response->ByteCount, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_CHECK_DIRECTORY,
|
||
0
|
||
);
|
||
} else {
|
||
|
||
//
|
||
// If the user didn't have this permission, update the
|
||
// statistics database.
|
||
//
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
if (CLIENT_CAPABLE_OF(NT_STATUS, WorkContext->Connection)) {
|
||
SrvSetSmbError( WorkContext, status );
|
||
} else {
|
||
SrvSetSmbError( WorkContext, STATUS_OBJECT_PATH_NOT_FOUND );
|
||
}
|
||
}
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbCheckDirectory complete.\n" );
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return SmbStatusSendResponse;
|
||
|
||
} // SrvSmbCheckDirectory
|