1170 lines
27 KiB
C
1170 lines
27 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991-1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
SsSubs.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains support routines for the NT server service.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 10-Jan-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "srvsvcp.h"
|
|||
|
#include "ssreg.h"
|
|||
|
|
|||
|
#include <lmerr.h>
|
|||
|
#include <lmsname.h>
|
|||
|
#include <netlibnt.h>
|
|||
|
#include <tstr.h>
|
|||
|
|
|||
|
#include <ctype.h>
|
|||
|
#include <stdarg.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#include <ntddnfs.h>
|
|||
|
|
|||
|
#define MRXSMB_DEVICE_NAME TEXT("\\Device\\LanmanRedirector")
|
|||
|
|
|||
|
|
|||
|
PSERVER_REQUEST_PACKET
|
|||
|
SsAllocateSrp (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine allocates a serer request packet so that an API can
|
|||
|
communicate with the kernel-mode server. Any general initialization
|
|||
|
in performed here.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PSERVER_REQUEST_PACKET - a pointer to the allocated SRP.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSERVER_REQUEST_PACKET srp;
|
|||
|
|
|||
|
srp = MIDL_user_allocate( sizeof(SERVER_REQUEST_PACKET) );
|
|||
|
if ( srp != NULL ) {
|
|||
|
RtlZeroMemory( srp, sizeof(SERVER_REQUEST_PACKET) );
|
|||
|
}
|
|||
|
|
|||
|
return srp;
|
|||
|
|
|||
|
} // SsAllocateSrp
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
VOID
|
|||
|
SsAssert(
|
|||
|
IN PVOID FailedAssertion,
|
|||
|
IN PVOID FileName,
|
|||
|
IN ULONG LineNumber
|
|||
|
)
|
|||
|
{
|
|||
|
BOOL ok;
|
|||
|
CHAR choice[16];
|
|||
|
DWORD bytes;
|
|||
|
DWORD error;
|
|||
|
|
|||
|
SsPrintf( "\nAssertion failed: %s\n at line %ld of %s\n",
|
|||
|
FailedAssertion, LineNumber, FileName );
|
|||
|
do {
|
|||
|
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|||
|
|
|||
|
SsPrintf( "Break or Ignore [bi]? " );
|
|||
|
|
|||
|
if (hStdIn && (hStdIn != INVALID_HANDLE_VALUE))
|
|||
|
{
|
|||
|
bytes = sizeof(choice);
|
|||
|
ok = ReadFile(
|
|||
|
hStdIn,
|
|||
|
&choice,
|
|||
|
bytes,
|
|||
|
&bytes,
|
|||
|
NULL
|
|||
|
);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// default to "break"
|
|||
|
ok = TRUE;
|
|||
|
choice[0] = TEXT('B');
|
|||
|
}
|
|||
|
|
|||
|
if ( ok ) {
|
|||
|
if ( toupper(choice[0]) == 'I' ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
if ( toupper(choice[0]) == 'B' ) {
|
|||
|
DbgUserBreakPoint( );
|
|||
|
}
|
|||
|
} else {
|
|||
|
error = GetLastError( );
|
|||
|
}
|
|||
|
} while ( TRUE );
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SsAssert
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SsCloseServer (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine closes the server file system device, if it has been
|
|||
|
opened.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Close the server device, if it has been opened.
|
|||
|
//
|
|||
|
|
|||
|
if ( SsData.SsServerDeviceHandle != NULL ) {
|
|||
|
NtClose( SsData.SsServerDeviceHandle );
|
|||
|
SsData.SsServerDeviceHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
} // SsCloseServer
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SsControlCHandler (
|
|||
|
IN ULONG CtrlType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Captures and ignores a kill signal. Without this, any ^C pressed in
|
|||
|
the window that started the server service will result in this
|
|||
|
process being killed, and then the server can't function properly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CtrlType;
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SsControlCHandler
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SsFreeSrp (
|
|||
|
IN PSERVER_REQUEST_PACKET Srp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Frees an SRP allocated by SsAllocateSrp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Srp - a pointer to the SRP to free.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
MIDL_user_free( Srp );
|
|||
|
|
|||
|
} // SsFreeSrp
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SsLogEvent(
|
|||
|
IN DWORD MessageId,
|
|||
|
IN DWORD NumberOfSubStrings,
|
|||
|
IN LPWSTR *SubStrings,
|
|||
|
IN DWORD ErrorCode
|
|||
|
)
|
|||
|
{
|
|||
|
HANDLE logHandle;
|
|||
|
DWORD dataSize = 0;
|
|||
|
LPVOID rawData = NULL;
|
|||
|
USHORT eventType = EVENTLOG_ERROR_TYPE;
|
|||
|
|
|||
|
logHandle = RegisterEventSource(
|
|||
|
NULL,
|
|||
|
SERVER_DISPLAY_NAME
|
|||
|
);
|
|||
|
|
|||
|
if ( logHandle == NULL ) {
|
|||
|
SS_PRINT(( "SRVSVC: RegisterEventSource failed: %lu\n",
|
|||
|
GetLastError() ));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if ( ErrorCode != NERR_Success ) {
|
|||
|
|
|||
|
//
|
|||
|
// An error code was specified.
|
|||
|
//
|
|||
|
|
|||
|
dataSize = sizeof(ErrorCode);
|
|||
|
rawData = (LPVOID)&ErrorCode;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the message is is only a warning, then set the event type as such.
|
|||
|
// This is doc'd in netevent.h.
|
|||
|
//
|
|||
|
|
|||
|
if ((ULONG)(MessageId & 0xC0000000) == (ULONG) 0x80000000 ) {
|
|||
|
|
|||
|
eventType = EVENTLOG_WARNING_TYPE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Log the error.
|
|||
|
//
|
|||
|
|
|||
|
if ( !ReportEventW(
|
|||
|
logHandle,
|
|||
|
eventType,
|
|||
|
0, // event category
|
|||
|
MessageId,
|
|||
|
NULL, // user SID
|
|||
|
(WORD)NumberOfSubStrings,
|
|||
|
dataSize,
|
|||
|
SubStrings,
|
|||
|
rawData
|
|||
|
) ) {
|
|||
|
SS_PRINT(( "SRVSVC: ReportEvent failed: %lu\n",
|
|||
|
GetLastError() ));
|
|||
|
}
|
|||
|
|
|||
|
if ( !DeregisterEventSource( logHandle ) ) {
|
|||
|
SS_PRINT(( "SRVSVC: DeregisterEventSource failed: %lu\n",
|
|||
|
GetLastError() ));
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SsLogEvent
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
SsOpenServer ()
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine opens the server file system device, allowing the
|
|||
|
server service to send FS controls to it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - results of operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
UNICODE_STRING unicodeServerName;
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Open the server device.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString( &unicodeServerName, SERVER_DEVICE_NAME );
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&objectAttributes,
|
|||
|
&unicodeServerName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Opening the server with desired access = SYNCHRONIZE and open
|
|||
|
// options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
|
|||
|
// to worry about waiting for the NtFsControlFile to complete--this
|
|||
|
// makes all IO system calls that use this handle synchronous.
|
|||
|
//
|
|||
|
|
|||
|
status = NtOpenFile(
|
|||
|
&SsData.SsServerDeviceHandle,
|
|||
|
FILE_ALL_ACCESS & ~SYNCHRONIZE,
|
|||
|
&objectAttributes,
|
|||
|
&ioStatusBlock,
|
|||
|
0,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS(status) ) {
|
|||
|
status = ioStatusBlock.Status;
|
|||
|
}
|
|||
|
|
|||
|
if ( !NT_SUCCESS(status) ) {
|
|||
|
IF_DEBUG(INITIALIZATION_ERRORS) {
|
|||
|
SS_PRINT(( "SsOpenServer: NtOpenFile (server device object) "
|
|||
|
"failed: %X\n", status ));
|
|||
|
}
|
|||
|
return NetpNtStatusToApiStatus( status );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We're now ready to talk to the server.
|
|||
|
//
|
|||
|
|
|||
|
return NO_ERROR;
|
|||
|
|
|||
|
} // SsOpenServer
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
VOID
|
|||
|
SsPrintf (
|
|||
|
char *Format,
|
|||
|
...
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
va_list arglist;
|
|||
|
char OutputBuffer[1024];
|
|||
|
ULONG length;
|
|||
|
HANDLE hStdOut;
|
|||
|
|
|||
|
va_start( arglist, Format );
|
|||
|
|
|||
|
vsprintf( OutputBuffer, Format, arglist );
|
|||
|
|
|||
|
va_end( arglist );
|
|||
|
|
|||
|
length = strlen( OutputBuffer );
|
|||
|
|
|||
|
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|||
|
|
|||
|
if (hStdOut && (hStdOut != INVALID_HANDLE_VALUE))
|
|||
|
{
|
|||
|
WriteFile(hStdOut, (LPVOID )OutputBuffer, length, &length, NULL );
|
|||
|
}
|
|||
|
|
|||
|
} // SsPrintf
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
SsServerFsControlGetInfo (
|
|||
|
IN ULONG ServerControlCode,
|
|||
|
IN PSERVER_REQUEST_PACKET Srp,
|
|||
|
IN OUT PVOID *OutputBuffer,
|
|||
|
IN ULONG PreferredMaximumLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sends an SRP to the server for an API that retrieves
|
|||
|
information from the server and takes a PreferredMaximumLength
|
|||
|
parameter.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerControlCode - the FSCTL code for the operation.
|
|||
|
|
|||
|
Srp - a pointer to the SRP for the operation.
|
|||
|
|
|||
|
OutputBuffer - a pointer to receive a pointer to the buffer
|
|||
|
allocated by this routine to hold the output information.
|
|||
|
|
|||
|
PreferredMaximumLength - the PreferredMaximumLength parameter.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - results of operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NET_API_STATUS status = STATUS_SUCCESS;
|
|||
|
ULONG resumeHandle = Srp->Parameters.Get.ResumeHandle;
|
|||
|
ULONG BufLen = min( INITIAL_BUFFER_SIZE, PreferredMaximumLength );
|
|||
|
ULONG i;
|
|||
|
|
|||
|
*OutputBuffer = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Normally, we should only go through this loop at most 2 times. But
|
|||
|
// if the amount of data the server needs to return is growing, then
|
|||
|
// we might be forced to run through it a couple of more times. '5'
|
|||
|
// is an arbitrary number just to ensure we don't get stuck.
|
|||
|
//
|
|||
|
|
|||
|
for( i=0; i < 5; i++ ) {
|
|||
|
|
|||
|
if( *OutputBuffer ) {
|
|||
|
MIDL_user_free( *OutputBuffer );
|
|||
|
}
|
|||
|
|
|||
|
*OutputBuffer = MIDL_user_allocate( BufLen );
|
|||
|
|
|||
|
if( *OutputBuffer == NULL ) {
|
|||
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make the request of the server.
|
|||
|
//
|
|||
|
|
|||
|
Srp->Parameters.Get.ResumeHandle = resumeHandle;
|
|||
|
|
|||
|
status = SsServerFsControl(
|
|||
|
ServerControlCode,
|
|||
|
Srp,
|
|||
|
*OutputBuffer,
|
|||
|
BufLen
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// If we were successful, or we got an error other than our buffer
|
|||
|
// being too small, break out.
|
|||
|
//
|
|||
|
if ( status != ERROR_MORE_DATA && status != NERR_BufTooSmall ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We've been told that our buffer isn't big enough. But if we've hit
|
|||
|
// the caller's PreferredMaximumLength, break out.
|
|||
|
//
|
|||
|
if( BufLen >= PreferredMaximumLength ) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Let's try again. EXTRA_ALLOCATION is here to cover the case where the
|
|||
|
// amount of space required grows between the previous FsControl call and
|
|||
|
// the next one.
|
|||
|
//
|
|||
|
BufLen = min( Srp->Parameters.Get.TotalBytesNeeded + EXTRA_ALLOCATION,
|
|||
|
PreferredMaximumLength );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if ( *OutputBuffer && Srp->Parameters.Get.EntriesRead == 0 ) {
|
|||
|
MIDL_user_free( *OutputBuffer );
|
|||
|
*OutputBuffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // SsServerFsControlGetInfo
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
SsServerFsControl (
|
|||
|
IN ULONG ServerControlCode,
|
|||
|
IN PSERVER_REQUEST_PACKET Srp OPTIONAL,
|
|||
|
IN PVOID Buffer,
|
|||
|
IN ULONG BufferLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sends an FSCTL to the server using the previously opened
|
|||
|
server handle
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ServerControlCode - the FSCTL code to send to the server.
|
|||
|
|
|||
|
Srp - a pointer to the SRP for the operation.
|
|||
|
|
|||
|
Buffer - a pointer to the buffer to pass to the server as the
|
|||
|
"OutputBuffer" parameter of NtFsControlFile.
|
|||
|
|
|||
|
BufferLength - the size of this buffer.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - results of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
NET_API_STATUS error;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
PSERVER_REQUEST_PACKET sendSrp;
|
|||
|
ULONG sendSrpLength;
|
|||
|
PWCH name1Buffer, name2Buffer;
|
|||
|
HANDLE eventHandle;
|
|||
|
|
|||
|
if( SsData.SsServerDeviceHandle == NULL ) {
|
|||
|
DbgPrint( "SRVSVC: SsData.SsServerDeviceHandle == NULL\n" );
|
|||
|
return ERROR_BAD_NET_RESP;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a name was specified, we must capture the SRP along with the
|
|||
|
// name in order to avoid sending embedded input pointers.
|
|||
|
//
|
|||
|
|
|||
|
if ( Srp != NULL ) {
|
|||
|
|
|||
|
name1Buffer = Srp->Name1.Buffer;
|
|||
|
name2Buffer = Srp->Name2.Buffer;
|
|||
|
|
|||
|
if ( Srp->Name1.Buffer != NULL || Srp->Name2.Buffer != NULL ) {
|
|||
|
|
|||
|
PCHAR nextStringLocation;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate enough space to hold the SRP + name.
|
|||
|
//
|
|||
|
|
|||
|
sendSrpLength = sizeof(SERVER_REQUEST_PACKET);
|
|||
|
|
|||
|
if ( Srp->Name1.Buffer != NULL ) {
|
|||
|
sendSrpLength += Srp->Name1.MaximumLength;
|
|||
|
}
|
|||
|
|
|||
|
if ( Srp->Name2.Buffer != NULL ) {
|
|||
|
sendSrpLength += Srp->Name2.MaximumLength;
|
|||
|
}
|
|||
|
|
|||
|
sendSrp = MIDL_user_allocate( sendSrpLength );
|
|||
|
|
|||
|
if ( sendSrp == NULL ) {
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy over the SRP.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( sendSrp, Srp, sizeof(SERVER_REQUEST_PACKET) );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the names in the new SRP.
|
|||
|
//
|
|||
|
|
|||
|
nextStringLocation = (PCHAR)( sendSrp + 1 );
|
|||
|
|
|||
|
if ( Srp->Name1.Buffer != NULL ) {
|
|||
|
|
|||
|
sendSrp->Name1.Length = Srp->Name1.Length;
|
|||
|
sendSrp->Name1.MaximumLength = Srp->Name1.MaximumLength;
|
|||
|
sendSrp->Name1.Buffer = (PWCH)nextStringLocation;
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
sendSrp->Name1.Buffer,
|
|||
|
Srp->Name1.Buffer,
|
|||
|
Srp->Name1.MaximumLength
|
|||
|
);
|
|||
|
|
|||
|
nextStringLocation += Srp->Name1.MaximumLength;
|
|||
|
|
|||
|
POINTER_TO_OFFSET( sendSrp->Name1.Buffer, sendSrp );
|
|||
|
}
|
|||
|
|
|||
|
if ( Srp->Name2.Buffer != NULL ) {
|
|||
|
|
|||
|
sendSrp->Name2.Length = Srp->Name2.Length;
|
|||
|
sendSrp->Name2.MaximumLength = Srp->Name2.MaximumLength;
|
|||
|
sendSrp->Name2.Buffer = (PWCH)nextStringLocation;
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
sendSrp->Name2.Buffer,
|
|||
|
Srp->Name2.Buffer,
|
|||
|
Srp->Name2.MaximumLength
|
|||
|
);
|
|||
|
|
|||
|
POINTER_TO_OFFSET( sendSrp->Name2.Buffer, sendSrp );
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// There was no name in the SRP, so just send the SRP that was
|
|||
|
// passed in.
|
|||
|
//
|
|||
|
|
|||
|
sendSrp = Srp;
|
|||
|
sendSrpLength = sizeof(SERVER_REQUEST_PACKET);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This request has no SRP.
|
|||
|
//
|
|||
|
|
|||
|
sendSrp = NULL;
|
|||
|
sendSrpLength = 0;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create an event to synchronize with the driver
|
|||
|
//
|
|||
|
status = NtCreateEvent(
|
|||
|
&eventHandle,
|
|||
|
FILE_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
NotificationEvent,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Send the request to the server FSD.
|
|||
|
//
|
|||
|
if( NT_SUCCESS( status ) ) {
|
|||
|
|
|||
|
status = NtFsControlFile(
|
|||
|
SsData.SsServerDeviceHandle,
|
|||
|
eventHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
ServerControlCode,
|
|||
|
sendSrp,
|
|||
|
sendSrpLength,
|
|||
|
Buffer,
|
|||
|
BufferLength
|
|||
|
);
|
|||
|
|
|||
|
if( status == STATUS_PENDING ) {
|
|||
|
NtWaitForSingleObject( eventHandle, FALSE, NULL );
|
|||
|
}
|
|||
|
|
|||
|
NtClose( eventHandle );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If an error code was set in the SRP, use it. Otherwise, if
|
|||
|
// an error was returned or set in the IO status block, use that.
|
|||
|
//
|
|||
|
|
|||
|
if ( (sendSrp != NULL) && (sendSrp->ErrorCode != NO_ERROR) ) {
|
|||
|
error = sendSrp->ErrorCode;
|
|||
|
IF_DEBUG(API_ERRORS) {
|
|||
|
SS_PRINT(( "SsServerFsControl: (1) API call %lx to srv failed, "
|
|||
|
"err = %ld\n", ServerControlCode, error ));
|
|||
|
}
|
|||
|
} else {
|
|||
|
if ( NT_SUCCESS(status) ) {
|
|||
|
status = ioStatusBlock.Status;
|
|||
|
}
|
|||
|
if ( status == STATUS_SERVER_HAS_OPEN_HANDLES ) {
|
|||
|
error = ERROR_SERVER_HAS_OPEN_HANDLES;
|
|||
|
} else {
|
|||
|
error = NetpNtStatusToApiStatus( status );
|
|||
|
}
|
|||
|
if ( error != NO_ERROR ) {
|
|||
|
IF_DEBUG(API_ERRORS) {
|
|||
|
SS_PRINT(( "SsServerFsControl: (2) API call %lx to srv "
|
|||
|
"failed, err = %ld, status = %X\n",
|
|||
|
ServerControlCode, error, status ));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a separate buffer was allocated to capture the name, copy
|
|||
|
// over the new SRP and free it.
|
|||
|
//
|
|||
|
|
|||
|
if ( sendSrp != Srp ) {
|
|||
|
RtlCopyMemory( Srp, sendSrp, sizeof(SERVER_REQUEST_PACKET) );
|
|||
|
Srp->Name1.Buffer = name1Buffer;
|
|||
|
Srp->Name2.Buffer = name2Buffer;
|
|||
|
MIDL_user_free( sendSrp );
|
|||
|
}
|
|||
|
|
|||
|
return error;
|
|||
|
|
|||
|
} // SsServerFsControl
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SsGetServerType (
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Return the ServiceBits of all the services implemented by this service.
|
|||
|
|
|||
|
Enter with SsData.SsServerInfoResource locked.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
SV_TYPE service bits.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD serviceBits;
|
|||
|
|
|||
|
serviceBits = SV_TYPE_SERVER | SV_TYPE_NT | SsData.ServiceBits;
|
|||
|
|
|||
|
if( SsData.IsDfsRoot ) {
|
|||
|
serviceBits |= SV_TYPE_DFS;
|
|||
|
}
|
|||
|
|
|||
|
if ( SsData.ServerInfo599.sv599_timesource ) {
|
|||
|
serviceBits |= SV_TYPE_TIME_SOURCE;
|
|||
|
}
|
|||
|
|
|||
|
if ( SsData.ServerInfo598.sv598_producttype == NtProductServer ) {
|
|||
|
serviceBits |= SV_TYPE_SERVER_NT;
|
|||
|
}
|
|||
|
|
|||
|
if ( SsData.NumberOfPrintShares != 0 ) {
|
|||
|
serviceBits |= SV_TYPE_PRINTQ_SERVER;
|
|||
|
}
|
|||
|
|
|||
|
return serviceBits;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SsSetExportedServerType (
|
|||
|
IN PNAME_LIST_ENTRY service OPTIONAL,
|
|||
|
IN BOOL ExternalBitsAlreadyChanged,
|
|||
|
IN BOOL UpdateImmediately
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD serviceBits;
|
|||
|
DWORD newServiceBits;
|
|||
|
BOOL changed = ExternalBitsAlreadyChanged;
|
|||
|
|
|||
|
//
|
|||
|
// The value returned in the sv102_type field is an amalgam of the
|
|||
|
// following:
|
|||
|
//
|
|||
|
// 1) The internal server type bits SV_TYPE_SERVER (always set),
|
|||
|
// SV_TYPE_NT (always set), SV_TYPE_TIME_SOURCE (set if the
|
|||
|
// parameter TimeSource is TRUE), and SV_TYPE_PRINTQ_SERVER (set
|
|||
|
// if there are any print shares).
|
|||
|
//
|
|||
|
// 2) SV_TYPE_DFS if this machine is the root of a DFS tree
|
|||
|
//
|
|||
|
// 3) The bits set by the service controller calling I_NetServerSetServiceBits.
|
|||
|
// SV_TYPE_TIME_SOURCE is a pseudo internal bit. It can be set
|
|||
|
// internally or it can be set by the w32time service.
|
|||
|
//
|
|||
|
// 4) The logical OR of all per-transport server type bits set by
|
|||
|
// the Browser calling I_NetServerSetServiceBits.
|
|||
|
//
|
|||
|
|
|||
|
(VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE );
|
|||
|
|
|||
|
serviceBits = SsGetServerType();
|
|||
|
|
|||
|
if( ARGUMENT_PRESENT( service ) ) {
|
|||
|
//
|
|||
|
// Change the bits for the passed-in NAME_LIST_ENTRY only
|
|||
|
//
|
|||
|
|
|||
|
newServiceBits = service->ServiceBits;
|
|||
|
newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS);
|
|||
|
newServiceBits |= serviceBits;
|
|||
|
|
|||
|
if( service->ServiceBits != newServiceBits ) {
|
|||
|
service->ServiceBits |= newServiceBits;
|
|||
|
changed = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
//
|
|||
|
// Change the bits for each NAME_LIST_ENTRY
|
|||
|
//
|
|||
|
for ( service = SsData.SsServerNameList; service != NULL; service = service->Next ) {
|
|||
|
|
|||
|
newServiceBits = service->ServiceBits;
|
|||
|
newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS );
|
|||
|
newServiceBits |= serviceBits;
|
|||
|
|
|||
|
if( service->ServiceBits != newServiceBits ) {
|
|||
|
service->ServiceBits |= newServiceBits;
|
|||
|
changed = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
|
|||
|
if ( changed && UpdateImmediately ) {
|
|||
|
if( SsData.SsStatusChangedEvent )
|
|||
|
{
|
|||
|
if ( !SetEvent( SsData.SsStatusChangedEvent ) ) {
|
|||
|
SS_PRINT(( "SsSetExportedServerType: SetEvent failed: %ld\n",
|
|||
|
GetLastError( ) ));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // SsSetExportedServerType
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS
|
|||
|
SsSetField (
|
|||
|
IN PFIELD_DESCRIPTOR Field,
|
|||
|
IN PVOID Value,
|
|||
|
IN BOOLEAN WriteToRegistry,
|
|||
|
OUT BOOLEAN *AnnouncementInformationChanged OPTIONAL
|
|||
|
)
|
|||
|
{
|
|||
|
PCHAR structure;
|
|||
|
|
|||
|
//
|
|||
|
// *** We do not initialize *AnnouncementInformationChanged to
|
|||
|
// FALSE! We leave it alone, unless interesting information is
|
|||
|
// changed, in which case we set it to TRUE. This is to allow a
|
|||
|
// caller to initialize it itself, then call this function
|
|||
|
// multiple times, with the resulting value in the parameter
|
|||
|
// being TRUE if at least one of the calls changed an
|
|||
|
// interesting parameter.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Determine the structure that will be set.
|
|||
|
//
|
|||
|
|
|||
|
if ( Field->Level / 100 == 5 ) {
|
|||
|
if ( Field->Level != 598 ) {
|
|||
|
structure = (PCHAR)&SsData.ServerInfo599;
|
|||
|
} else {
|
|||
|
structure = (PCHAR)&SsData.ServerInfo598;
|
|||
|
}
|
|||
|
} else {
|
|||
|
structure = (PCHAR)&SsData.ServerInfo102;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the value in the field based on the field type.
|
|||
|
//
|
|||
|
|
|||
|
switch ( Field->FieldType ) {
|
|||
|
|
|||
|
case BOOLEAN_FIELD: {
|
|||
|
|
|||
|
BOOLEAN value = *(PBOOLEAN)Value;
|
|||
|
PBOOLEAN valueLocation;
|
|||
|
|
|||
|
//
|
|||
|
// BOOLEANs may only be TRUE (1) or FALSE (0).
|
|||
|
//
|
|||
|
|
|||
|
if ( value != TRUE && value != FALSE ) {
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
valueLocation = (PBOOLEAN)( structure + Field->FieldOffset );
|
|||
|
|
|||
|
//
|
|||
|
// If we're turning off Hidden (i.e., making the server public),
|
|||
|
// indicate that an announcment-related parameter has changed.
|
|||
|
// This will cause an announcement to be sent immediately.
|
|||
|
//
|
|||
|
|
|||
|
if ( (Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_102, sv102_hidden )) &&
|
|||
|
(value && !(*valueLocation)) &&
|
|||
|
(ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
|
|||
|
*AnnouncementInformationChanged = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
*valueLocation = value;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DWORD_FIELD: {
|
|||
|
|
|||
|
DWORD value = *(PDWORD)Value;
|
|||
|
PDWORD valueLocation;
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the specified value is in the range of
|
|||
|
// legal values for the Field.
|
|||
|
//
|
|||
|
|
|||
|
if ( value > Field->MaximumValue || value < Field->MinimumValue ) {
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
valueLocation = (PDWORD)( structure + Field->FieldOffset );
|
|||
|
*valueLocation = value;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case LPSTR_FIELD: {
|
|||
|
|
|||
|
LPWCH value = *(LPWCH *)Value;
|
|||
|
LPWSTR valueLocation;
|
|||
|
ULONG maxLength;
|
|||
|
|
|||
|
//
|
|||
|
// We are setting the name, comment, or userpath for the server.
|
|||
|
// Use the field offset to determine which.
|
|||
|
//
|
|||
|
|
|||
|
if ( Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_102, sv102_name ) ) {
|
|||
|
valueLocation = SsData.ServerNameBuffer;
|
|||
|
maxLength = sizeof( SsData.SsServerTransportAddress );
|
|||
|
} else if ( Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_102, sv102_comment ) ) {
|
|||
|
valueLocation = SsData.ServerCommentBuffer;
|
|||
|
maxLength = MAXCOMMENTSZ;
|
|||
|
} else if ( Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_102, sv102_userpath ) ) {
|
|||
|
valueLocation = SsData.UserPathBuffer;
|
|||
|
maxLength = MAX_PATH;
|
|||
|
} else if ( Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_599, sv599_domain ) ) {
|
|||
|
valueLocation = SsData.DomainNameBuffer;
|
|||
|
maxLength = DNLEN;
|
|||
|
} else {
|
|||
|
SS_ASSERT( FALSE );
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the string is too long, return an error.
|
|||
|
//
|
|||
|
|
|||
|
if ( (value != NULL) && (STRLEN(value) > maxLength) ) {
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we're changing the server comment, indicate that an
|
|||
|
// announcment-related parameter has changed. This will cause
|
|||
|
// an announcement to be sent immediately.
|
|||
|
//
|
|||
|
|
|||
|
if ( (Field->FieldOffset ==
|
|||
|
FIELD_OFFSET( SERVER_INFO_102, sv102_comment )) &&
|
|||
|
( ((value == NULL) && (*valueLocation != '\0')) ||
|
|||
|
((value != NULL) && (wcscmp(value,valueLocation) != 0)) ) &&
|
|||
|
(ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
|
|||
|
*AnnouncementInformationChanged = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the input is NULL, make the string zero length.
|
|||
|
//
|
|||
|
|
|||
|
if ( value == NULL ) {
|
|||
|
|
|||
|
*valueLocation = '\0';
|
|||
|
*(valueLocation+1) = '\0';
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
wcscpy( valueLocation, value );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
} // end switch
|
|||
|
|
|||
|
//
|
|||
|
// The change worked. If requested, add the parameter to the
|
|||
|
// registry, thus effecting a sticky change. Don't write it
|
|||
|
// to the registry if this is xxx_comment or xxx_disc since
|
|||
|
// we already write out their more well known aliases
|
|||
|
// srvcomment and autodisconnect. Changes here should also be
|
|||
|
// made to SetStickyParameters().
|
|||
|
//
|
|||
|
|
|||
|
if ( WriteToRegistry &&
|
|||
|
(_wcsicmp( Field->FieldName, DISC_VALUE_NAME ) != 0) &&
|
|||
|
(_wcsicmp( Field->FieldName, COMMENT_VALUE_NAME ) != 0) ) {
|
|||
|
|
|||
|
SsAddParameterToRegistry( Field, Value );
|
|||
|
}
|
|||
|
|
|||
|
return NO_ERROR;
|
|||
|
|
|||
|
} // SsSetField
|
|||
|
|
|||
|
UINT
|
|||
|
SsGetDriveType (
|
|||
|
IN LPWSTR path
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine calls GetDriveType, attempting to eliminate
|
|||
|
the DRIVE_NO_ROOT_DIR type
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
A path
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The drive type
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UINT driveType = GetDriveType( path );
|
|||
|
|
|||
|
if( driveType == DRIVE_NO_ROOT_DIR ) {
|
|||
|
|
|||
|
if( path[0] != UNICODE_NULL && path[1] == L':' ) {
|
|||
|
|
|||
|
WCHAR shortPath[ 4 ];
|
|||
|
|
|||
|
shortPath[0] = path[0];
|
|||
|
shortPath[1] = L':';
|
|||
|
shortPath[2] = L'\\';
|
|||
|
shortPath[3] = L'\0';
|
|||
|
|
|||
|
driveType = GetDriveType( shortPath );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ULONG len = wcslen( path );
|
|||
|
LPWSTR pathWithSlash = MIDL_user_allocate( (len + 2) * sizeof( *path ) );
|
|||
|
|
|||
|
if( pathWithSlash != NULL ) {
|
|||
|
RtlCopyMemory( pathWithSlash, path, len * sizeof( *path ) );
|
|||
|
pathWithSlash[ len ] = L'\\';
|
|||
|
pathWithSlash[ len+1 ] = L'\0';
|
|||
|
driveType = GetDriveType( pathWithSlash );
|
|||
|
MIDL_user_free( pathWithSlash );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return driveType;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SsNotifyRdrOfGuid(
|
|||
|
LPGUID Guid
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
HANDLE hMrxSmbHandle;
|
|||
|
UNICODE_STRING unicodeServerName;
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
//
|
|||
|
// Open the server device.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString( &unicodeServerName, MRXSMB_DEVICE_NAME );
|
|||
|
|
|||
|
InitializeObjectAttributes(
|
|||
|
&objectAttributes,
|
|||
|
&unicodeServerName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Opening the server with desired access = SYNCHRONIZE and open
|
|||
|
// options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
|
|||
|
// to worry about waiting for the NtFsControlFile to complete--this
|
|||
|
// makes all IO system calls that use this handle synchronous.
|
|||
|
//
|
|||
|
|
|||
|
status = NtOpenFile(
|
|||
|
&hMrxSmbHandle,
|
|||
|
FILE_ALL_ACCESS & ~SYNCHRONIZE,
|
|||
|
&objectAttributes,
|
|||
|
&ioStatusBlock,
|
|||
|
0,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS(status) ) {
|
|||
|
status = NtFsControlFile( hMrxSmbHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
FSCTL_LMR_SET_SERVER_GUID,
|
|||
|
Guid,
|
|||
|
sizeof(GUID),
|
|||
|
NULL,
|
|||
|
0 );
|
|||
|
|
|||
|
NtClose( hMrxSmbHandle );
|
|||
|
}
|
|||
|
}
|