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 );
|
||
}
|
||
}
|