676 lines
16 KiB
C
676 lines
16 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991-1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
SrvInfo.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains support for the server get and set info APIs
|
|||
|
in the server service.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David Treadwell (davidtr) 7-Mar-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "srvsvcp.h"
|
|||
|
#include "ssreg.h"
|
|||
|
|
|||
|
#include <netlibnt.h>
|
|||
|
|
|||
|
#include <tstr.h>
|
|||
|
#include <lmerr.h>
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS NET_API_FUNCTION
|
|||
|
NetrServerGetInfo (
|
|||
|
IN LPWSTR ServerName,
|
|||
|
IN DWORD Level,
|
|||
|
OUT LPSERVER_INFO InfoStruct
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine uses the server parameters stored in the server service
|
|||
|
to return the server information.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - NO_ERROR or reason for failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG outputBufferLength;
|
|||
|
NET_API_STATUS error;
|
|||
|
ACCESS_MASK desiredAccess;
|
|||
|
LPWSTR DomainName;
|
|||
|
PNAME_LIST_ENTRY service;
|
|||
|
PTRANSPORT_LIST_ENTRY transport;
|
|||
|
UCHAR serverNameBuf[ MAX_PATH ];
|
|||
|
UNICODE_STRING ServerNameUnicode;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG namelen;
|
|||
|
|
|||
|
//
|
|||
|
// Determine the access required for the requested level of
|
|||
|
// information.
|
|||
|
//
|
|||
|
|
|||
|
switch ( Level ) {
|
|||
|
|
|||
|
case 100:
|
|||
|
case 101:
|
|||
|
|
|||
|
desiredAccess = SRVSVC_CONFIG_USER_INFO_GET;
|
|||
|
break;
|
|||
|
|
|||
|
case 102:
|
|||
|
case 502:
|
|||
|
|
|||
|
desiredAccess = SRVSVC_CONFIG_POWER_INFO_GET;
|
|||
|
break;
|
|||
|
|
|||
|
case 503:
|
|||
|
|
|||
|
desiredAccess = SRVSVC_CONFIG_ADMIN_INFO_GET;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return ERROR_INVALID_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the caller has that level of access.
|
|||
|
//
|
|||
|
|
|||
|
error = SsCheckAccess(
|
|||
|
&SsConfigInfoSecurityObject,
|
|||
|
desiredAccess
|
|||
|
);
|
|||
|
|
|||
|
if ( error != NO_ERROR ) {
|
|||
|
return ERROR_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the resource that protects server information. Since
|
|||
|
// we'll only read the information, get shared access to the
|
|||
|
// resource.
|
|||
|
//
|
|||
|
|
|||
|
(VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
|
|||
|
|
|||
|
if( ServerName == NULL ) {
|
|||
|
ServerName = SsData.ServerNameBuffer;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Convert the server name
|
|||
|
//
|
|||
|
|
|||
|
if( ServerName[0] == L'\\' && ServerName[1] == L'\\' ) {
|
|||
|
ServerName += 2;
|
|||
|
}
|
|||
|
|
|||
|
RtlInitUnicodeString( &ServerNameUnicode, ServerName );
|
|||
|
error = ConvertStringToTransportAddress( &ServerNameUnicode, serverNameBuf, &namelen );
|
|||
|
if( error != NERR_Success ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Look for the NAME_LIST_ENTRY entry that represents the name of the server
|
|||
|
// the client referred to.
|
|||
|
//
|
|||
|
|
|||
|
DomainName = SsData.DomainNameBuffer;
|
|||
|
|
|||
|
for( service = SsData.SsServerNameList; service != NULL; service = service->Next ) {
|
|||
|
|
|||
|
if( service->TransportAddressLength != namelen ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if( RtlEqualMemory( serverNameBuf, service->TransportAddress, namelen ) ) {
|
|||
|
DomainName = service->DomainName;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we didn't find an entry, find and use the primary entry
|
|||
|
//
|
|||
|
if( service == NULL ) {
|
|||
|
for( service = SsData.SsServerNameList; service != NULL; service = service->Next ) {
|
|||
|
if( service->PrimaryName ) {
|
|||
|
DomainName = service->DomainName;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Use the level parameter to determine how much space to allocate
|
|||
|
// and how to fill it in.
|
|||
|
//
|
|||
|
|
|||
|
switch ( Level ) {
|
|||
|
|
|||
|
case 100: {
|
|||
|
|
|||
|
PSERVER_INFO_100 sv100;
|
|||
|
|
|||
|
//
|
|||
|
// All we copy is the server name.
|
|||
|
//
|
|||
|
|
|||
|
outputBufferLength = sizeof(SERVER_INFO_100) +
|
|||
|
STRSIZE( ServerName);
|
|||
|
|
|||
|
sv100 = MIDL_user_allocate( outputBufferLength );
|
|||
|
if ( sv100 == NULL ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy over the fixed portion of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( sv100, &SsData.ServerInfo102, sizeof(SERVER_INFO_100) );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the name string.
|
|||
|
//
|
|||
|
|
|||
|
sv100->sv100_name = (LPWSTR)( sv100 + 1 );
|
|||
|
STRCPY( sv100->sv100_name, ServerName );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the output buffer pointer.
|
|||
|
//
|
|||
|
|
|||
|
InfoStruct->ServerInfo100 = sv100;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case 101: {
|
|||
|
|
|||
|
PSERVER_INFO_101 sv101;
|
|||
|
|
|||
|
//
|
|||
|
// All we copy is the server name.
|
|||
|
//
|
|||
|
|
|||
|
outputBufferLength = sizeof(SERVER_INFO_101) +
|
|||
|
STRSIZE( ServerName ) +
|
|||
|
STRSIZE( SsData.ServerCommentBuffer ) ;
|
|||
|
|
|||
|
sv101 = MIDL_user_allocate( outputBufferLength );
|
|||
|
if ( sv101 == NULL ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy over the fixed portion of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( sv101, &SsData.ServerInfo102, sizeof(SERVER_INFO_101) );
|
|||
|
|
|||
|
if( service != NULL ) {
|
|||
|
sv101->sv101_type = service->ServiceBits;
|
|||
|
for( transport = service->Transports; transport; transport = transport->Next ) {
|
|||
|
sv101->sv101_type |= transport->ServiceBits;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there are no transports,
|
|||
|
// return the global information.
|
|||
|
//
|
|||
|
|
|||
|
sv101->sv101_type = SsGetServerType();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Set up the variable portion of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
sv101->sv101_name = (LPWSTR)( sv101 + 1 );
|
|||
|
STRCPY( sv101->sv101_name, ServerName );
|
|||
|
|
|||
|
sv101->sv101_comment = (LPWSTR)( (PCHAR)sv101->sv101_name +
|
|||
|
STRSIZE( ServerName ));
|
|||
|
STRCPY( sv101->sv101_comment, SsData.ServerCommentBuffer );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the output buffer pointer.
|
|||
|
//
|
|||
|
|
|||
|
InfoStruct->ServerInfo101 = sv101;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case 102: {
|
|||
|
|
|||
|
PSERVER_INFO_102 sv102;
|
|||
|
|
|||
|
//
|
|||
|
// We copy the server name, server comment, and user path
|
|||
|
// buffer.
|
|||
|
//
|
|||
|
|
|||
|
outputBufferLength = sizeof(SERVER_INFO_102) +
|
|||
|
STRSIZE( ServerName ) +
|
|||
|
STRSIZE( SsData.ServerCommentBuffer ) +
|
|||
|
STRSIZE( SsData.UserPathBuffer ) ;
|
|||
|
|
|||
|
sv102 = MIDL_user_allocate( outputBufferLength );
|
|||
|
if ( sv102 == NULL ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy over the fixed portion of the buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( sv102, &SsData.ServerInfo102, sizeof(SERVER_INFO_102) );
|
|||
|
|
|||
|
if( service != NULL ) {
|
|||
|
sv102->sv102_type = service->ServiceBits;
|
|||
|
for( transport = service->Transports; transport; transport = transport->Next ) {
|
|||
|
sv102->sv102_type |= transport->ServiceBits;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// If there are no transports,
|
|||
|
// return the global information.
|
|||
|
//
|
|||
|
|
|||
|
sv102->sv102_type = SsGetServerType();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up the server name.
|
|||
|
//
|
|||
|
|
|||
|
sv102->sv102_name = (LPWSTR)( sv102 + 1 );
|
|||
|
STRCPY( sv102->sv102_name, ServerName );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the server comment.
|
|||
|
//
|
|||
|
|
|||
|
sv102->sv102_comment = (LPWSTR)( (PCHAR)sv102->sv102_name + STRSIZE( ServerName ));
|
|||
|
STRCPY( sv102->sv102_comment, SsData.ServerCommentBuffer );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the user path.
|
|||
|
//
|
|||
|
|
|||
|
sv102->sv102_userpath = (LPWSTR)( (PCHAR)sv102->sv102_comment +
|
|||
|
STRSIZE( sv102->sv102_comment ) );
|
|||
|
STRCPY( sv102->sv102_userpath, SsData.UserPathBuffer );
|
|||
|
|
|||
|
//
|
|||
|
// Set up the output buffer pointer.
|
|||
|
//
|
|||
|
|
|||
|
InfoStruct->ServerInfo102 = sv102;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case 502:
|
|||
|
|
|||
|
//
|
|||
|
// Allocate enough space to hold the fixed structure. This level has
|
|||
|
// no variable structure.
|
|||
|
//
|
|||
|
|
|||
|
InfoStruct->ServerInfo502 = MIDL_user_allocate( sizeof(SERVER_INFO_502) );
|
|||
|
if ( InfoStruct->ServerInfo502 == NULL ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the data from the server service buffer to the user buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
InfoStruct->ServerInfo502,
|
|||
|
&SsData.ServerInfo599,
|
|||
|
sizeof(SERVER_INFO_502)
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case 503: {
|
|||
|
|
|||
|
PSERVER_INFO_503 sv503;
|
|||
|
|
|||
|
outputBufferLength = sizeof( *sv503 ) + STRSIZE( DomainName );
|
|||
|
|
|||
|
sv503 = MIDL_user_allocate( outputBufferLength );
|
|||
|
|
|||
|
if ( sv503 == NULL ) {
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the data from the server service buffer to the user buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( sv503, &SsData.ServerInfo599, sizeof( *sv503 ) );
|
|||
|
|
|||
|
//
|
|||
|
// Copy the domain name
|
|||
|
//
|
|||
|
sv503->sv503_domain = (LPWSTR)( sv503 + 1 );
|
|||
|
STRCPY( sv503->sv503_domain, DomainName );
|
|||
|
|
|||
|
InfoStruct->ServerInfo503 = sv503;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
return ERROR_INVALID_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
|
|||
|
return NO_ERROR;
|
|||
|
|
|||
|
} // NetrServerGetInfo
|
|||
|
|
|||
|
|
|||
|
NET_API_STATUS NET_API_FUNCTION
|
|||
|
NetrServerSetInfo (
|
|||
|
IN LPWSTR ServerName,
|
|||
|
IN DWORD Level,
|
|||
|
IN LPSERVER_INFO InfoStruct,
|
|||
|
OUT LPDWORD ErrorParameter OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets information in the server service and server.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NET_API_STATUS - NO_ERROR or reason for failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NET_API_STATUS error;
|
|||
|
ULONG i;
|
|||
|
LONG parmnum;
|
|||
|
BOOLEAN validLevel = FALSE;
|
|||
|
PSERVER_REQUEST_PACKET srp;
|
|||
|
LPBYTE buffer = (LPBYTE)InfoStruct->ServerInfo100;
|
|||
|
BOOLEAN announcementInformationChanged = FALSE;
|
|||
|
|
|||
|
ServerName;
|
|||
|
|
|||
|
//
|
|||
|
// Check that user input buffer is not NULL
|
|||
|
//
|
|||
|
if (buffer == NULL) {
|
|||
|
if ( ARGUMENT_PRESENT( ErrorParameter ) ) {
|
|||
|
*ErrorParameter = PARM_ERROR_UNKNOWN;
|
|||
|
}
|
|||
|
return ERROR_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
parmnum = (LONG)(Level - PARMNUM_BASE_INFOLEVEL);
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT( ErrorParameter ) ) {
|
|||
|
*ErrorParameter = parmnum;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure that the caller is allowed to set information in the
|
|||
|
// server.
|
|||
|
//
|
|||
|
|
|||
|
error = SsCheckAccess(
|
|||
|
&SsConfigInfoSecurityObject,
|
|||
|
SRVSVC_CONFIG_INFO_SET
|
|||
|
);
|
|||
|
|
|||
|
if ( error != NO_ERROR ) {
|
|||
|
return ERROR_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the resource that protects server information. Since
|
|||
|
// we're going to be writing to the information, we need exclusive
|
|||
|
// access to the reqource.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If a parameter number was specified, set that one field.
|
|||
|
//
|
|||
|
|
|||
|
if ( parmnum >= 0 ) {
|
|||
|
|
|||
|
//
|
|||
|
// Walk through the field descriptors looking for an
|
|||
|
// equivalent parameter number.
|
|||
|
//
|
|||
|
|
|||
|
for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
|
|||
|
|
|||
|
if ( (ULONG)parmnum == SsServerInfoFields[i].ParameterNumber ) {
|
|||
|
|
|||
|
//
|
|||
|
// Verify that the field is settable.
|
|||
|
//
|
|||
|
// !!! We should also reject levels above 502?
|
|||
|
//
|
|||
|
|
|||
|
if ( SsServerInfoFields[i].Settable != ALWAYS_SETTABLE ) {
|
|||
|
return ERROR_INVALID_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
(VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Set the field.
|
|||
|
//
|
|||
|
|
|||
|
error = SsSetField(
|
|||
|
&SsServerInfoFields[i],
|
|||
|
buffer,
|
|||
|
TRUE,
|
|||
|
&announcementInformationChanged
|
|||
|
);
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
|
|||
|
//
|
|||
|
// If a relevant parameter changed, call
|
|||
|
// SsSetExportedServerType. This will cause an
|
|||
|
// announcement to be sent.
|
|||
|
//
|
|||
|
|
|||
|
if ( announcementInformationChanged ) {
|
|||
|
SsSetExportedServerType( NULL, TRUE, TRUE );
|
|||
|
}
|
|||
|
|
|||
|
return error;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a match had been found we would have returned by now.
|
|||
|
// Indicate that the parameter number was illegal.
|
|||
|
//
|
|||
|
|
|||
|
return ERROR_INVALID_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// A full input structure was specified. Walk through all the
|
|||
|
// server data field descriptors, looking for fields that should be
|
|||
|
// set.
|
|||
|
//
|
|||
|
|
|||
|
for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) {
|
|||
|
|
|||
|
ULONG fieldLevel;
|
|||
|
|
|||
|
//
|
|||
|
// We need to set this field if:
|
|||
|
//
|
|||
|
// o the level specified on input is the same order as the
|
|||
|
// level of the field. They have the same order if
|
|||
|
// they are in the same century (e.g. 101 and 102 are
|
|||
|
// in the same order); AND
|
|||
|
//
|
|||
|
// o the specified level is greater than or equal to the
|
|||
|
// level of the field. For example, if the input
|
|||
|
// level is 101 and the field level is 102, don't set
|
|||
|
// the field. If the input level is 102 and the field
|
|||
|
// level is 101, set it; AND
|
|||
|
//
|
|||
|
// o the field is settable. If the field is not settable
|
|||
|
// by NetServerSetInfo, just ignore the value in the
|
|||
|
// input structure.
|
|||
|
//
|
|||
|
// Note that level 598 doesn't follow the first rule above. It
|
|||
|
// is NOT a superset of 50x, and it is NOT a subset of 599.
|
|||
|
//
|
|||
|
|
|||
|
fieldLevel = SsServerInfoFields[i].Level;
|
|||
|
|
|||
|
if ( Level / 100 == fieldLevel / 100 &&
|
|||
|
((fieldLevel != 598) && (Level >= fieldLevel) ||
|
|||
|
(fieldLevel == 598) && (Level == 598)) &&
|
|||
|
SsServerInfoFields[i].Settable == ALWAYS_SETTABLE ) {
|
|||
|
|
|||
|
//
|
|||
|
// We found a match, so the specified level number must have
|
|||
|
// been valid.
|
|||
|
//
|
|||
|
// !!! Reject levels above 502?
|
|||
|
|
|||
|
validLevel = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Set this field.
|
|||
|
//
|
|||
|
|
|||
|
(VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE );
|
|||
|
|
|||
|
error = SsSetField(
|
|||
|
&SsServerInfoFields[i],
|
|||
|
buffer + SsServerInfoFields[i].FieldOffset,
|
|||
|
TRUE,
|
|||
|
&announcementInformationChanged
|
|||
|
);
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
|
|||
|
if ( error != NO_ERROR ) {
|
|||
|
|
|||
|
//
|
|||
|
// Set the parameter in error if we need to.
|
|||
|
//
|
|||
|
|
|||
|
if ( ARGUMENT_PRESENT(ErrorParameter) ) {
|
|||
|
*ErrorParameter = SsServerInfoFields[i].ParameterNumber;
|
|||
|
}
|
|||
|
|
|||
|
return error;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If no match was ever found, then an invalid level was passed in.
|
|||
|
//
|
|||
|
|
|||
|
if ( !validLevel ) {
|
|||
|
return ERROR_INVALID_LEVEL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get an SRP and set it up with the appropriate level.
|
|||
|
//
|
|||
|
|
|||
|
srp = SsAllocateSrp( );
|
|||
|
if ( srp == NULL ) {
|
|||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
srp->Level = 0xFFFFFFFF;
|
|||
|
|
|||
|
(VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Send the request on to the server.
|
|||
|
//
|
|||
|
|
|||
|
error = SsServerFsControl(
|
|||
|
FSCTL_SRV_NET_SERVER_SET_INFO,
|
|||
|
srp,
|
|||
|
&SsData.ServerInfo102,
|
|||
|
sizeof(SERVER_INFO_102) + sizeof(SERVER_INFO_599) +
|
|||
|
sizeof(SERVER_INFO_598)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Release the resource and free the SRP.
|
|||
|
//
|
|||
|
|
|||
|
RtlReleaseResource( &SsData.SsServerInfoResource );
|
|||
|
|
|||
|
SsFreeSrp( srp );
|
|||
|
|
|||
|
//
|
|||
|
// If a relevant parameter changed, call SsSetExportedServerType.
|
|||
|
// This will cause an announcement to be sent.
|
|||
|
//
|
|||
|
|
|||
|
if ( announcementInformationChanged ) {
|
|||
|
SsSetExportedServerType( NULL, TRUE, TRUE );
|
|||
|
}
|
|||
|
|
|||
|
return error;
|
|||
|
|
|||
|
} // NetrServerSetInfo
|