windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/srvsvc/server/registry.c

2992 lines
75 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
registry.c
Abstract:
This module contains registry _access routines for the NT server
service.
Author:
Chuck Lenzmeier (chuckl) 19-Mar-1992
Revision History:
--*/
#include "srvsvcp.h"
#include "ssreg.h"
#include "srvconfg.h"
#include <tstr.h>
#include <netevent.h>
//
// Simple MIN and MAX macros. Watch out for side effects!
//
#define MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
#define MAX(a,b) ( ((a) < (b)) ? (b) : (a) )
#define MAX_INTEGER_STRING 32
#define MB * 1024 * 1024
#define INF 0xffffffff
//
// ( u, n )
// u is units to allocate for every n megabytes on a medium server.
//
#define CONFIG_TUPLE_SIZE 2
typedef struct {
DWORD initworkitems[CONFIG_TUPLE_SIZE];
DWORD maxworkitems[CONFIG_TUPLE_SIZE];
DWORD rawworkitems[CONFIG_TUPLE_SIZE];
DWORD maxrawworkitems[CONFIG_TUPLE_SIZE];
DWORD maxpagedmemoryusage[CONFIG_TUPLE_SIZE];
DWORD maxnonpagedmemoryusage[CONFIG_TUPLE_SIZE];
} CONFIG_SERVER_TABLE;
CONFIG_SERVER_TABLE MedSrvCfgTbl = {
//
// ** NOTE ** : If the second column is greater than 4, then
// you will need to add a check to make sure the statistic
// did not drop to zero.
//
// Units / MB
// Parameter
// ---------
//
/* initworkitems */ { 1 , 4 },
/* maxworkitems */ { 4 , 1 },
/* rawworkitems */ { 1 , 4 },
/* maxrawworkitems */ { 4 , 1 },
/* maxpagedmemoryusage */ { 1 , 1 },
/* maxnonpagedmemoryusage */ { 1 , 8 },
};
//
// Minimum configuration system size is 8MB. Anything lower treated
// as if 8 MB.
//
#define MIN_SYSTEM_SIZE 8
//
// A medium server reaches its max at 32M. A small server at 16M.
//
#define MAX_SMALL_SIZE 16
#define MAX_MEDIUM_SIZE 32
//
// Note that the user limit is always -1 (unlimited). Autodisconnect
// always defaults to 15 minutes.
//
//
// Forward declarations
//
NTSTATUS
EnumerateStickyShare (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
NET_API_STATUS
FillStickyShareInfo(
IN PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo,
IN PSHARE_INFO_502 Shi502
);
NTSTATUS
GetSdFromRegistry(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
BOOLEAN
GetStickyShareInfo (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
OUT PUNICODE_STRING RemarkString,
OUT PUNICODE_STRING PathString,
OUT PSHARE_INFO_502 shi502,
OUT PDWORD CacheState
);
LONG
LoadParameters (
PWCH Path
);
LONG
LoadSizeParameter (
VOID
);
NTSTATUS
RecreateStickyShare (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
NTSTATUS
SaveSdToRegistry(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PWSTR ShareName
);
NTSTATUS
SetSizeParameters (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
NTSTATUS
SetStickyParameter (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
#define IsPersonal() IsSuiteVersion(VER_SUITE_PERSONAL)
#define IsWebBlade() IsSuiteVersion(VER_SUITE_BLADE)
#define IsEmbedded() IsSuiteVersion(VER_SUITE_EMBEDDEDNT)
BOOL
IsSuiteVersion(USHORT SuiteMask)
{
OSVERSIONINFOEX Osvi;
DWORD TypeMask;
DWORDLONG ConditionMask;
memset(&Osvi, 0, sizeof(OSVERSIONINFOEX));
Osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
Osvi.wSuiteMask = SuiteMask;
TypeMask = VER_SUITENAME;
ConditionMask = 0;
VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_OR);
return(VerifyVersionInfo(&Osvi, TypeMask, ConditionMask));
}
ULONG
SsRtlQueryEnvironmentLength (
IN PVOID Environment
)
{
PWCH p;
ULONG length;
p = Environment;
ASSERT( p != NULL );
//
// The environment variable block consists of zero or more null
// terminated ASCII strings. Each string is of the form:
//
// name=value
//
// where the null termination is after the value.
//
while ( *p ) {
while ( *p ) {
p++;
}
p++;
}
p++;
length = (ULONG)((PCHAR)p - (PCHAR)Environment);
//
// Return accumulated length.
//
return length;
}
VOID
SsAddParameterToRegistry (
PFIELD_DESCRIPTOR Field,
PVOID Value
)
{
NTSTATUS status;
PWCH valueName;
DWORD valueType;
LPBYTE valuePtr;
DWORD valueDataLength;
//
// The value name is the parameter name and the value data is the
// parameter value.
//
valueName = Field->FieldName;
switch ( Field->FieldType ) {
case BOOLEAN_FIELD:
case DWORD_FIELD:
valueType = REG_DWORD;
valuePtr = Value;
valueDataLength = sizeof(DWORD);
break;
case LPSTR_FIELD:
valueType = REG_SZ;
valuePtr = *(LPBYTE *)Value;
if ( valuePtr != NULL ) {
valueDataLength = SIZE_WSTR( (PWCH)valuePtr );
} else {
valueDataLength = 0;
}
break;
}
//
// Set the value into the Parameters key.
//
status = RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES,
PARAMETERS_REGISTRY_PATH,
valueName,
valueType,
valuePtr,
valueDataLength
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddParameterToRegistry: SetValue failed: %lx; "
"parameter %ws won't stick\n", status, valueName ));
}
}
return;
} // SsAddParameterToRegistry
VOID
SsAddShareToRegistry (
IN PSHARE_INFO_2 ShareInfo2,
IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
IN DWORD CacheState
)
{
NTSTATUS status;
PWCH valueName;
PVOID environment;
UNICODE_STRING nameString;
UNICODE_STRING valueString;
WCHAR integerString[MAX_INTEGER_STRING + 1];
ULONG environmentLength;
//
// Build the value name and data strings. The value name is the
// share name (netname), while the value data is share information
// in REG_MULTI_SZ format. To build the value data, we use the
// RTL environment routines.
//
valueName = ShareInfo2->shi2_netname;
status = RtlCreateEnvironment( FALSE, &environment );
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: CreateEnvironment failed: %lx; "
"share %ws won't stick\n", status, valueName ));
}
goto exit1;
}
RtlInitUnicodeString( &nameString, PATH_VARIABLE_NAME );
RtlInitUnicodeString( &valueString, ShareInfo2->shi2_path );
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
if ( ShareInfo2->shi2_remark != NULL ) {
RtlInitUnicodeString( &nameString, REMARK_VARIABLE_NAME );
RtlInitUnicodeString( &valueString, ShareInfo2->shi2_remark );
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
}
RtlInitUnicodeString( &nameString, TYPE_VARIABLE_NAME );
valueString.Buffer = integerString;
valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
status = RtlIntegerToUnicodeString(
ShareInfo2->shi2_type,
10,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
"share %ws won't stick\n", status, valueName ));
}
goto exit2;
}
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
RtlInitUnicodeString( &nameString, PERMISSIONS_VARIABLE_NAME );
valueString.Buffer = integerString;
valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
status = RtlIntegerToUnicodeString(
ShareInfo2->shi2_permissions,
10,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
"share %ws won't stick\n", status, valueName ));
}
goto exit2;
}
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
RtlInitUnicodeString( &nameString, MAXUSES_VARIABLE_NAME );
valueString.Buffer = integerString;
valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
status = RtlIntegerToUnicodeString(
ShareInfo2->shi2_max_uses,
10,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
"share %ws won't stick\n", status, valueName ));
}
goto exit2;
}
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
//
// Set the CacheState
//
RtlInitUnicodeString( &nameString, CSC_VARIABLE_NAME );
valueString.Buffer = integerString;
valueString.MaximumLength = (MAX_INTEGER_STRING + 1) * sizeof(WCHAR);
status = RtlIntegerToUnicodeString(
CacheState,
10,
&valueString
);
if( !NT_SUCCESS( status ) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: IntegerToUnicode failed: %lx; "
"share %ws won't stick\n", status, valueName ));
}
goto exit2;
}
status = RtlSetEnvironmentVariable(
&environment,
&nameString,
&valueString
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetEnvironment failed: %lx; "
"share %s won't stick\n", status, valueName ));
}
goto exit2;
}
//
// Set the value into the Shares key.
//
environmentLength = SsRtlQueryEnvironmentLength( environment );
status = RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES,
SHARES_REGISTRY_PATH,
valueName,
REG_MULTI_SZ,
(LPBYTE)environment,
environmentLength
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SetValue failed: %lx; share %ws "
"won't stick\n", status, valueName ));
}
}
//
// Save the file security descriptor
//
if ( ARGUMENT_PRESENT( SecurityDescriptor ) ) {
status = SaveSdToRegistry(
SecurityDescriptor,
valueName
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsAddShareToRegistry: SaveSd failed: %lx; share %ws\n"
, status, valueName ));
}
}
}
exit2:
RtlDestroyEnvironment( environment );
exit1:
return;
} // SsAddShareToRegistry
NET_API_STATUS
SsCheckRegistry (
VOID
)
/*++
Routine Description:
This function verifies that the keys used by the server exist.
Arguments:
None.
Return Value:
NET_API_STATUS - success/failure of the operation.
--*/
{
NTSTATUS status;
LPWSTR subStrings[1];
//
// Verify the existence of the main server service key. If this
// fails, the server service fails to start.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
SERVER_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = SERVER_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_FOUND,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: main key doesn't exist\n" ));
}
return ERROR_INVALID_PARAMETER; // !!! Need better error
}
//
// Verify the existence of the Linkage subkey. If this fails, the
// server service fails to start.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
LINKAGE_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = LINKAGE_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_FOUND,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Linkage subkey doesn't exist\n" ));
}
return ERROR_INVALID_PARAMETER; // !!! Need better error
}
//
// If the Parameters subkey doesn't exist, create it. If it can't
// be created, fail to start the server.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
PARAMETERS_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
status = RtlCreateRegistryKey(
RTL_REGISTRY_SERVICES,
PARAMETERS_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = PARAMETERS_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_CREATED,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Can't create Parameters subkey: "
"%lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
}
//
// Create the key holding the default security descriptors governing server APIs.
// Since we have compiled-in versions for these APIs, it is a non-fatal error
// if we cannot create this key. But we log it anyway.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_DEFAULT_SECURITY_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
status = RtlCreateRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_DEFAULT_SECURITY_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = SHARES_DEFAULT_SECURITY_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_CREATED,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Can't create DefaultSecurityRegistry subkey: "
"%lx\n", status ));
}
}
}
{
LONG error;
HKEY handle;
GUID Guid;
//
// Make sure the GUID_VARIABLE_NAME value is there and contains a valid GUID.
//
error = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
FULL_PARAMETERS_REGISTRY_PATH,
0,
KEY_ALL_ACCESS,
&handle
);
if( error == ERROR_SUCCESS ) {
DWORD type;
DWORD size = sizeof( Guid );
error = RegQueryValueEx( handle,
GUID_VARIABLE_NAME,
NULL,
&type,
(LPBYTE)&Guid,
&size
);
if( error != ERROR_SUCCESS ||
type != REG_BINARY ||
size != sizeof( Guid ) ) {
RPC_STATUS RpcStatus;
//
// We could not read it, or it's not a valid UUID.
// Blow it away and reset
//
RegDeleteValue( handle, GUID_VARIABLE_NAME );
RpcStatus = UuidCreate( &Guid );
if( RpcStatus == RPC_S_OK || RpcStatus == RPC_S_UUID_LOCAL_ONLY ) {
error = RegSetValueEx( handle,
GUID_VARIABLE_NAME,
0,
REG_BINARY,
(LPBYTE)&Guid,
sizeof( Guid )
);
}
SsNotifyRdrOfGuid( &Guid );
}
RegCloseKey( handle );
} else {
RtlZeroMemory( &Guid, sizeof( Guid ) );
}
SsData.ServerInfo598.sv598_serverguid = Guid;
}
//
// If the AutotunedParameters subkey doesn't exist, create it. If
// it can't be created, fail to start the server.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
AUTOTUNED_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
status = RtlCreateRegistryKey(
RTL_REGISTRY_SERVICES,
AUTOTUNED_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = AUTOTUNED_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_CREATED,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Can't create AutotunedParameters "
"subkey: %lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
}
//
// If the Shares subkey doesn't exist, create it. If it can't be
// created, fail to start the server.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
status = RtlCreateRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_CREATED,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Can't create Shares subkey: "
"%lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
}
//
// If the Shares Security subkey doesn't exist, create it. If it
// can't be created, fail to start the server.
//
status = RtlCheckRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_SECURITY_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
status = RtlCreateRegistryKey(
RTL_REGISTRY_SERVICES,
SHARES_SECURITY_REGISTRY_PATH
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = SHARES_SECURITY_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_KEY_NOT_CREATED,
1,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsCheckRegistry: Can't create Shares Security subkey: "
"%lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
}
//
// All keys successfully checked.
//
return NO_ERROR;
} // SsCheckRegistry
NET_API_STATUS
SsEnumerateStickyShares (
IN OUT PSRVSVC_SHARE_ENUM_INFO ShareEnumInfo
)
/*++
Routine Description:
Reads the registry to find and return sticky shares.
Arguments:
ShareEnumInfo - points to a structure that contains the parameters
to the NetShareEnumSticky call.
Return Value:
NET_API_STATUS - success/failure of the operation.
--*/
{
NTSTATUS status;
PRTL_QUERY_REGISTRY_TABLE queryTable;
ShareEnumInfo->TotalBytesNeeded = 0;
ShareEnumInfo->TotalEntries = 0;
ShareEnumInfo->EntriesRead = 0;
//
// Initialize the reserve fields. This tells the callback routine,
// how many times it has been called.
//
ShareEnumInfo->ShareEnumIndex = 0;
ShareEnumInfo->StartOfFixedData = (PCHAR)ShareEnumInfo->OutputBuffer;
ShareEnumInfo->EndOfVariableData = (PCHAR)ShareEnumInfo->OutputBuffer +
ShareEnumInfo->OutputBufferLength;
//
// We need to align it since we deal with unicode strings.
//
ShareEnumInfo->EndOfVariableData =
(PCHAR)((ULONG_PTR)ShareEnumInfo->EndOfVariableData & ~1);
//
// Ask the RTL to call us back for each value in the Shares key.
//
queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
if ( queryTable != NULL ) {
queryTable[0].QueryRoutine = EnumerateStickyShare;
queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
queryTable[0].Name = NULL;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
SHARES_REGISTRY_PATH,
queryTable,
ShareEnumInfo,
NULL
);
MIDL_user_free( queryTable );
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsEnumerateStickyShares: RtlQueryRegistryValues "
"failed: %lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
return NO_ERROR;
} // SsEnumerateStickyShares
NET_API_STATUS
SsLoadConfigurationParameters (
VOID
)
/*++
Routine Description:
Reads the registry to get server configuration parameters. These
server parameters must be set before the server FSP has been
started.
Arguments:
None.
Return Value:
NET_API_STATUS - success/failure of the operation.
--*/
{
LONG error;
//
// Get the basic Size parameter, then load autotuned parameters,
// then load manually set parameters. This ordering allows manual
// settings to override autotuning.
//
error = LoadSizeParameter( );
if ( error == NO_ERROR ) {
error = LoadParameters( AUTOTUNED_REGISTRY_PATH );
if ( error == NO_ERROR ) {
error = LoadParameters( PARAMETERS_REGISTRY_PATH );
}
}
//
// The copy read to MDL read switchover must occur at or below the
// SMB buffer size.
//
SsData.ServerInfo598.sv598_mdlreadswitchover =
MIN(
SsData.ServerInfo598.sv598_mdlreadswitchover,
SsData.ServerInfo599.sv599_sizreqbuf);
//
// If they want to require security signatures, it implies enabling them
//
if( SsData.ServerInfo598.sv598_requiresecuritysignature )
{
SsData.ServerInfo598.sv598_enablesecuritysignature = TRUE;
}
//
// Override parameters that cannot be set on WinNT (vs. NTAS).
//
// The server itself also performs most of these overrides, in case
// somebody figures out the FSCTL that changes parameters. We also
// override in the service in order to keep the service's view
// consistent with the server's. If you make any changes here, also
// make them in srv\svcsrv.c.
//
// Embedded does its own parameter validation, so skip it here
if( !IsEmbedded() )
{
if ( SsData.ServerInfo598.sv598_producttype == NtProductWinNt ) {
//
// On WinNT, the maximum value of certain parameters is fixed at
// build time. These include: concurrent users, SMB buffers,
// and threads.
//
#define MINIMIZE(_param,_max) _param = MIN( _param, _max );
MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_WKSTA );
MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_WKSTA );
MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_WKSTA );
SsData.ServerInfo599.sv599_maxmpxct = DEF_MAXMPXCT_WKSTA;
if( IsPersonal() )
{
MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_PERSONAL );
}
//
// On WinNT, we do not cache closed RFCBs.
//
SsData.ServerInfo598.sv598_cachedopenlimit = 0;
//
// Sharing of redirected drives is not allowed on WinNT.
//
SsData.ServerInfo599.sv599_enablesharednetdrives = FALSE;
}
if( IsWebBlade() )
{
MINIMIZE( SsData.ServerInfo102.sv102_users, MAX_USERS_WEB_BLADE );
MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_WKSTA );
MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_WKSTA );
}
}
else
{
// If this is a Class 1 basic embedded device, keep our memory consumption lower too
if( SsData.ServerInfo102.sv102_users == MAX_USERS_EMBEDDED )
{
MINIMIZE( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS_EMBEDDED );
MINIMIZE( SsData.ServerInfo598.sv598_maxthreadsperqueue, MAX_THREADS_EMBEDDED );
SsData.ServerInfo599.sv599_maxmpxct = DEF_MAXMPXCT_EMBEDDED;
}
}
return error;
} // SsLoadConfigurationParameters
NET_API_STATUS
SsRecreateStickyShares (
VOID
)
/*++
Routine Description:
Reads the registry to find and create sticky shares.
Arguments:
None.
Return Value:
NET_API_STATUS - success/failure of the operation.
--*/
{
NTSTATUS status;
PRTL_QUERY_REGISTRY_TABLE queryTable;
ULONG IterationCount = 0;
//
// Ask the RTL to call us back for each value in the Shares key.
//
queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
if ( queryTable != NULL ) {
queryTable[0].QueryRoutine = RecreateStickyShare;
queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
queryTable[0].Name = NULL;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
SHARES_REGISTRY_PATH,
queryTable,
&IterationCount,
NULL
);
MIDL_user_free( queryTable );
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsRecreateStickyShares: RtlQueryRegistryValues "
"failed: %lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
return NO_ERROR;
} // SsRecreateStickyShares
NET_API_STATUS
SsRemoveShareFromRegistry (
LPWSTR NetName
)
{
NET_API_STATUS error = NO_ERROR;
NTSTATUS status;
PWCH valueName;
//
// The value name is the share name. Remove that value from the
// Shares key.
//
valueName = NetName;
//
// Delete the share security
//
status = RtlDeleteRegistryValue(
RTL_REGISTRY_SERVICES,
SHARES_SECURITY_REGISTRY_PATH,
valueName
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsRemoveShareFromRegistry: Delete Security value failed: %lx; "
"share %ws will return\n", status, valueName ));
}
}
//
// Delete the share
//
status = RtlDeleteRegistryValue(
RTL_REGISTRY_SERVICES,
SHARES_REGISTRY_PATH,
valueName
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SsRemoveShareFromRegistry: DeleteValue failed: %lx; "
"share %ws will return\n", status, valueName ));
}
error = RtlNtStatusToDosError( status );
}
return error;
} // SsRemoveShareFromRegistry
VOID
BindToTransport (
IN LPWSTR TransportName
)
{
NET_API_STATUS error;
SERVER_TRANSPORT_INFO_0 svti0;
RtlZeroMemory( &svti0, sizeof( svti0 ) );
svti0.svti0_transportname = TransportName;
svti0.svti0_transportaddress = SsData.SsServerTransportAddress;
svti0.svti0_transportaddresslength =
ComputeTransportAddressClippedLength(
SsData.SsServerTransportAddress,
SsData.SsServerTransportAddressLength );
//
// Bind to the transport.
//
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "BindToTransport: binding to transport %ws\n", TransportName ));
}
error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&svti0 );
if ( error != NO_ERROR ) {
DWORD eventId;
LPWSTR subStrings[2];
IF_DEBUG(INITIALIZATION_ERRORS) {
SS_PRINT(( "SsBindToTransports: failed to bind to %ws: "
"%ld\n", TransportName, error ));
}
eventId = (error == ERROR_DUP_NAME || error == ERROR_INVALID_NETNAME ) ?
EVENT_SRV_CANT_BIND_DUP_NAME :
EVENT_SRV_CANT_BIND_TO_TRANSPORT;
subStrings[0] = TransportName;
SsLogEvent(
eventId,
1,
subStrings,
error
);
}
} // BindToTransport
NTSTATUS
BindOptionalNameToTransport (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
{
SERVER_TRANSPORT_INFO_0 sti;
UCHAR serverName[ MAX_PATH ];
UNICODE_STRING UnicodeName;
NET_API_STATUS error;
LPWSTR subStrings[2];
ULONG namelen;
subStrings[0] = (LPWSTR)ValueData;
subStrings[1] = OPTIONAL_NAMES_VALUE_NAME;
if( ValueType != REG_SZ ) {
//
// Not a string!
//
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
return STATUS_SUCCESS;
}
UnicodeName.Length = wcslen( (LPWSTR)ValueData ) * sizeof( WCHAR );
UnicodeName.MaximumLength = UnicodeName.Length + sizeof( WCHAR );
UnicodeName.Buffer = (LPWSTR)ValueData;
error = ConvertStringToTransportAddress( &UnicodeName, serverName, &namelen );
if( error != NO_ERROR ) {
//
// Invalid server name!
//
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
error
);
return STATUS_SUCCESS;
}
RtlZeroMemory( &sti, sizeof(sti) );
sti.svti0_transportname = (LPWSTR)Context;
sti.svti0_transportaddress = serverName;
sti.svti0_transportaddresslength = namelen;
error = I_NetrServerTransportAddEx( 0, (LPTRANSPORT_INFO)&sti );
if ( error != NO_ERROR ) {
//
// Could not register the name!
//
subStrings[0] = (LPWSTR)ValueData;
subStrings[1] = OPTIONAL_NAMES_VALUE_NAME;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
error
);
}
return STATUS_SUCCESS;
}
VOID
BindOptionalNames (
IN PWSTR TransportName
)
{
RTL_QUERY_REGISTRY_TABLE queryTable[2];
NTSTATUS status;
//
// We need to iterate over the optional names and bind them to this
// transport.
//
//
// Now see if there any optional bindings we should perform
//
queryTable[0].QueryRoutine = BindOptionalNameToTransport;
queryTable[0].Flags = 0;
queryTable[0].Name = OPTIONAL_NAMES_VALUE_NAME;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
(void)RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
PARAMETERS_REGISTRY_PATH,
queryTable,
TransportName,
NULL
);
} // BindOptionalNames
NTSTATUS
EnumerateStickyShare (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
Callback routine for SsEnumerateStickyShare. Routine will get information
on share and fill in the output buffer.
Arguments:
ValueName - Name of the share
ValueType - Value type of the share name.
ValueData - Data associated with the ValueName.
Context - Pointer to our enum information structure.
Return Value:
NET_API_STATUS - success/failure of the operation.
--*/
{
NET_API_STATUS error;
SHARE_INFO_502 shi502;
UNICODE_STRING pathString;
UNICODE_STRING remarkString;
PSRVSVC_SHARE_ENUM_INFO enumInfo = (PSRVSVC_SHARE_ENUM_INFO) Context;
DWORD cacheState;
ValueLength, EntryContext;
remarkString.Buffer = NULL;
pathString.Buffer = NULL;
if ( GetStickyShareInfo(
ValueName,
ValueType,
ValueData,
&remarkString,
&pathString,
&shi502,
&cacheState
) ) {
//
// Do the actual add of the share.
//
IF_DEBUG(REGISTRY) {
SS_PRINT(( "EnumerateStickyShares: adding share %ws\n", ValueName ));
}
shi502.shi502_remark = remarkString.Buffer;
shi502.shi502_path = pathString.Buffer;
//
// Skip until we have the right share to resume from
//
if ( (enumInfo->TotalEntries == 0) &&
(enumInfo->ShareEnumIndex < enumInfo->ResumeHandle) ) {
enumInfo->ShareEnumIndex++;
} else {
enumInfo->TotalEntries++;
error = FillStickyShareInfo( enumInfo, &shi502 );
if ( error != NO_ERROR ) {
IF_DEBUG(REGISTRY) {
SS_PRINT(( "EnumerateStickyShares: failed to add share "
"%ws = %wZ: %ld\n", ValueName, &pathString, error ));
}
} else {
enumInfo->EntriesRead++;
enumInfo->ResumeHandle++;
}
}
//
// free buffers allocated by GetStickyShareInfo
//
if ( remarkString.Buffer != NULL ) {
RtlFreeUnicodeString( &remarkString );
}
if ( pathString.Buffer != NULL ) {
RtlFreeUnicodeString( &pathString );
}
if ( shi502.shi502_security_descriptor != NULL ) {
MIDL_user_free( shi502.shi502_security_descriptor );
}
}
return STATUS_SUCCESS;
} // EnumerateStickyShare
NTSTATUS
GetSdFromRegistry(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
{
NTSTATUS status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR fileSD = NULL;
PSHARE_INFO_502 shi502 = (PSHARE_INFO_502) Context;
LPWSTR subStrings[1];
EntryContext, ValueName, ValueType;
if ( ValueLength > 0 ) {
fileSD = MIDL_user_allocate( ValueLength );
if ( fileSD == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopyMemory(
fileSD,
ValueData,
ValueLength
);
if ( !RtlValidSecurityDescriptor( fileSD ) ) {
subStrings[0] = ValueName;
SsLogEvent(
EVENT_SRV_INVALID_SD,
1,
subStrings,
RtlNtStatusToDosError( status )
);
MIDL_user_free( fileSD );
fileSD = NULL;
status = STATUS_INVALID_SECURITY_DESCR;
}
}
}
shi502->shi502_security_descriptor = fileSD;
return(status);
} // GetSdFromRegistry
BOOLEAN
GetStickyShareInfo (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
OUT PUNICODE_STRING RemarkString,
OUT PUNICODE_STRING PathString,
OUT PSHARE_INFO_502 shi502,
OUT PDWORD CacheState
)
/*++
Routine Description:
Gets share information from the registry.
Arguments:
ValueName - Name of the share
ValueType - Value type of the share name.
ValueData - Data associated with the ValueName.
RemarkString - Upon return, points to a unicode string containing the
user remark for this share.
PathString - Upon return, points to a unicode string containing the
path for this share.
shi502 - Upon return, points to a unicode string containing a
SHARE_INFO_502 structure.
Return Value:
TRUE, if share information successfully retrieved.
FALSE, otherwise.
--*/
{
NTSTATUS status;
UNICODE_STRING variableNameString;
WCHAR integerStringBuffer[35];
UNICODE_STRING unicodeString;
LPWSTR subStrings[2];
PathString->Buffer = NULL;
RemarkString->Buffer = NULL;
shi502->shi502_security_descriptor = NULL;
shi502->shi502_path = NULL;
shi502->shi502_remark = NULL;
shi502->shi502_reserved = 0;
//
// Because the NT server doesn't support share-level security, the
// password is always NULL.
//
shi502->shi502_passwd = NULL;
//
// The value type must be REG_MULTI_SZ, and the value name must not
// be null.
//
if ( (ValueType != REG_MULTI_SZ) ||
(wcslen(ValueName) == 0) ) {
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: skipping invalid value %ws\n",
ValueName ));
}
goto errorexit;
}
//
// The share name is the value name. The value data describes the
// rest of the information about the share.
//
shi502->shi502_netname = ValueName;
//
// The REG_MULTI_SZ format is the same as that used for storing
// environment variables. Find known share parameters in the data.
//
// Get the share path. It must be present.
//
RtlInitUnicodeString( &variableNameString, PATH_VARIABLE_NAME );
PathString->MaximumLength = 0;
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
PathString
);
if ( status != STATUS_BUFFER_TOO_SMALL ) {
//
// The path is not specified. Ignore this share.
//
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: No path; ignoring share.\n" ));
}
goto errorexit;
}
PathString->MaximumLength = (USHORT)(PathString->Length + sizeof(WCHAR));
PathString->Buffer = MIDL_user_allocate( PathString->MaximumLength );
if ( PathString->Buffer == NULL ) {
//
// No space for path. Ignore this share.
//
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
ERROR_NOT_ENOUGH_MEMORY
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring "
"share.\n" ));
}
goto errorexit;
}
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
PathString
);
if ( !NT_SUCCESS(status) ) {
//
// Huh? The second attempt failed. Ignore this share.
//
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: Second query failed! Ignoring "
"share.\n" ));
}
goto errorexit;
}
//
// Get the remark. It may be omitted.
//
RtlInitUnicodeString( &variableNameString, REMARK_VARIABLE_NAME );
RemarkString->MaximumLength = 0;
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
RemarkString
);
if ( status == STATUS_BUFFER_TOO_SMALL ) {
RemarkString->MaximumLength =
(USHORT)(RemarkString->Length + sizeof(WCHAR));
RemarkString->Buffer =
MIDL_user_allocate( RemarkString->MaximumLength );
if ( RemarkString->Buffer == NULL ) {
//
// No space for remark. Ignore this share.
//
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
ERROR_NOT_ENOUGH_MEMORY
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: MIDL_user_allocate failed; ignoring "
"share.\n" ));
}
goto errorexit;
}
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
RemarkString
);
if ( !NT_SUCCESS(status) ) {
//
// Huh? The second attempt failed. Ignore this share.
//
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: Second query failed! "
"Ignoring share.\n" ));
}
goto errorexit;
}
}
//
// Get the share type. It may be omitted.
//
RtlInitUnicodeString( &variableNameString, TYPE_VARIABLE_NAME );
unicodeString.Buffer = integerStringBuffer;
unicodeString.MaximumLength = 35;
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
&unicodeString
);
if ( !NT_SUCCESS(status) ) {
shi502->shi502_type = STYPE_DISKTREE;
} else {
status = RtlUnicodeStringToInteger(
&unicodeString,
0,
&shi502->shi502_type
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: "
"%lx\n", status ));
}
goto errorexit;
}
}
//
// Get the share permissions. It may be omitted.
//
RtlInitUnicodeString( &variableNameString, PERMISSIONS_VARIABLE_NAME );
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
&unicodeString
);
if ( !NT_SUCCESS(status) ) {
shi502->shi502_permissions = 0;
} else {
DWORD permissions;
status = RtlUnicodeStringToInteger(
&unicodeString,
0,
&permissions
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: UnicodeToInteger failed: "
"%lx\n", status ));
}
goto errorexit;
}
shi502->shi502_permissions = permissions;
}
//
// Get the maximum number of uses allowed. It may be omitted.
//
RtlInitUnicodeString( &variableNameString, MAXUSES_VARIABLE_NAME );
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
&unicodeString
);
if ( !NT_SUCCESS(status) ) {
shi502->shi502_max_uses = (DWORD)SHI_USES_UNLIMITED;
} else {
status = RtlUnicodeStringToInteger(
&unicodeString,
0,
&shi502->shi502_max_uses
);
if ( !NT_SUCCESS(status) ) {
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
goto errorexit;
}
}
//
// Get the Cacheing flags. It may be omitted.
//
RtlInitUnicodeString( &variableNameString, CSC_VARIABLE_NAME );
*CacheState = 0;
status = RtlQueryEnvironmentVariable_U(
ValueData,
&variableNameString,
&unicodeString
);
if( NT_SUCCESS( status ) ) {
ULONG value;
status = RtlUnicodeStringToInteger(
&unicodeString,
0,
&value
);
if( NT_SUCCESS( status ) ) {
*CacheState = (value & CSC_MASK);
} else {
subStrings[0] = ValueName;
subStrings[1] = SHARES_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
RtlNtStatusToDosError( status )
);
}
}
{
//
// Get the Share file security descriptor
//
RTL_QUERY_REGISTRY_TABLE shareQueryTable[2];
//
// Fill up the query table
//
shareQueryTable[0].QueryRoutine = GetSdFromRegistry;
shareQueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
shareQueryTable[0].Name = shi502->shi502_netname;
shareQueryTable[0].EntryContext = NULL;
shareQueryTable[0].DefaultType = REG_NONE;
shareQueryTable[1].QueryRoutine = NULL;
shareQueryTable[1].Flags = 0;
shareQueryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
SHARES_SECURITY_REGISTRY_PATH,
shareQueryTable,
shi502,
NULL
);
if ( !NT_SUCCESS( status) &&
( status != STATUS_OBJECT_NAME_NOT_FOUND ) ) {
ASSERT(0);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "GetStickyShareInfo: Get file SD: "
"%lx\n", status ));
}
goto errorexit;
}
}
return TRUE;
errorexit:
if ( RemarkString->Buffer != NULL ) {
RtlFreeUnicodeString( RemarkString );
}
if ( PathString->Buffer != NULL ) {
RtlFreeUnicodeString( PathString );
}
if ( shi502->shi502_security_descriptor != NULL ) {
MIDL_user_free( shi502->shi502_security_descriptor );
}
return FALSE;
} // GetStickyShareInfo
BOOLEAN
SsGetDefaultSdFromRegistry (
IN PWCH ValueName,
OUT PSECURITY_DESCRIPTOR *FileSD
)
/*++
Routine Description:
Reads 'ValueName' from the registry and gets the security descriptor
stored there.
Arguments:
ValueName - The name of the registry value in the Parameters section holding the descriptor
FileSD - points to the allocated SD if one was obtained
Return Value:
NTSTATUS - success/failure of the operation.
--*/
{
RTL_QUERY_REGISTRY_TABLE queryTable[2];
SHARE_INFO_502 shi502 = {0};
NTSTATUS status;
*FileSD = NULL;
queryTable[0].QueryRoutine = GetSdFromRegistry;
queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
queryTable[0].Name = ValueName;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
SHARES_DEFAULT_SECURITY_REGISTRY_PATH,
queryTable,
&shi502,
NULL
);
if ( NT_SUCCESS( status) ) {
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "SsGetDefaultSdFromRegistry: using %ws SD from registry.\n",
ValueName ));
}
*FileSD = shi502.shi502_security_descriptor;
return TRUE;
}
return FALSE;
}
VOID
SsWriteDefaultSdToRegistry (
IN PWCH ValueName,
IN PSECURITY_DESCRIPTOR FileSD
)
/*++
Routine Description:
Stores FileSD to 'ValueName' in the registry
Arguments:
ValueName - The name of the registry value in the Parameters section holding the descriptor
FileSD - points to the SD to write
--*/
{
ULONG fileSDLength;
if ( RtlValidSecurityDescriptor( FileSD ) ) {
fileSDLength = RtlLengthSecurityDescriptor( FileSD );
RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES,
SHARES_DEFAULT_SECURITY_REGISTRY_PATH,
ValueName,
REG_BINARY,
(LPBYTE)FileSD,
fileSDLength
);
}
}
LONG
LoadParameters (
PWCH Path
)
/*++
Routine Description:
Reads the registry to get server parameters.
Arguments:
Path - PARAMETERS_REGISTRY_PATH or AUTOTUNED_REGISTRY_PATH
Return Value:
LONG - success/failure of the operation.
--*/
{
NTSTATUS status;
PRTL_QUERY_REGISTRY_TABLE queryTable;
ULONG numberOfBindings = 0;
//
// Ask the RTL to call us back for each value in the appropriate
// key.
//
queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
if ( queryTable != NULL ) {
queryTable[0].QueryRoutine = SetStickyParameter;
queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
queryTable[0].Name = NULL;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
Path,
queryTable,
Path, // Context for SetStickyParameter
NULL
);
MIDL_user_free( queryTable );
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "LoadParameters: RtlQueryRegistryValues failed: "
"%lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
return NO_ERROR;
} // LoadParameters
LONG
LoadSizeParameter (
VOID
)
/*++
Routine Description:
Reads the registry to get the basic server Size parameter.
Arguments:
None.
Return Value:
LONG - success/failure of the operation.
--*/
{
NTSTATUS status;
PRTL_QUERY_REGISTRY_TABLE queryTable;
ULONG numberOfBindings = 0;
//
// Ask the RTL to call us back if the Size parameter exists.
//
queryTable = MIDL_user_allocate( sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 );
if ( queryTable != NULL ) {
queryTable[0].QueryRoutine = SetSizeParameters;
queryTable[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
queryTable[0].Name = SIZE_VALUE_NAME;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
status = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES | RTL_REGISTRY_OPTIONAL,
PARAMETERS_REGISTRY_PATH,
queryTable,
NULL,
NULL
);
MIDL_user_free( queryTable );
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "LoadSizeParameter: RtlQueryRegistryValues failed: "
"%lx\n", status ));
}
return RtlNtStatusToDosError( status );
}
return NO_ERROR;
} // LoadSizeParameter
VOID
PrintShareAnnounce (
LPVOID event
)
{
ULONG i;
//
// Announce ourselves and then wait for awhile.
// If the event gets signaled, terminate the loop and this thread.
// But don't do this forever, since the print subsystem may actually
// get stuck
//
//
// Do it for 15 minutes
//
for( i=0; i < 60; i++ ) {
AnnounceServiceStatus( 1 );
if( WaitForSingleObject( (HANDLE)event, 15*1000 ) != WAIT_TIMEOUT ) {
break;
}
}
}
NTSTATUS
RecreateStickyShare (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PULONG IterationCount,
IN PVOID EntryContext
)
{
NET_API_STATUS error;
SHARE_INFO_502 shi502;
SHARE_INFO shareInfo;
UNICODE_STRING pathString;
UNICODE_STRING remarkString;
HANDLE threadHandle = NULL;
HANDLE event = NULL;
DWORD CacheState;
SHARE_INFO shareInfoBuffer;
SHARE_INFO_1005 si1005;
ValueLength, EntryContext;
remarkString.Buffer = NULL;
pathString.Buffer = NULL;
if ( GetStickyShareInfo(
ValueName,
ValueType,
ValueData,
&remarkString,
&pathString,
&shi502,
&CacheState
) ) {
//
// Do the actual add of the share.
//
IF_DEBUG(INITIALIZATION) {
SS_PRINT(( "RecreateStickyShares: adding share %ws\n", ValueName ));
}
shi502.shi502_remark = remarkString.Buffer;
shi502.shi502_path = pathString.Buffer;
shareInfo.ShareInfo502 = (LPSHARE_INFO_502_I)&shi502;
if( shi502.shi502_type == STYPE_PRINTQ ) {
//
// A really big problem is that FAX printers can take aribitrarily long to
// complete the eventual OpenPrinter() call which the server will make back
// up to srvsvc. And if we don't announce ourselves in the interval, the
// service controller will presume that we got stuck on startup. Since
// NetrShareAdd() is synchronous, we need to get a different thread to
// announce our service status until NetrShareAdd returns. So, start it
// now. This is most unfortunate.
event = CreateEvent( NULL, TRUE, FALSE, NULL );
if( event != NULL ) {
DWORD threadId;
threadHandle = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)PrintShareAnnounce,
(LPVOID)event,
0,
&threadId
);
if( threadHandle == NULL ) {
CloseHandle( event );
event = NULL;
}
}
}
//
// RecreateStickyShare is called during server initialization. The service
// controller will presume that we're stuck if we don't update our status
// with it often enough. So every 64 recreated shares we call back to it.
// There's nothing magic about the 64 -- easy to check for, and not too often.
//
if( (shi502.shi502_type == STYPE_PRINTQ && threadHandle == NULL) ||
(++(*IterationCount) & 63 ) == 0 ) {
AnnounceServiceStatus( 1 );
}
error = NetrShareAdd( NULL, 502, &shareInfo, NULL );
if( event != NULL ) {
//
// We created an announcement thread, set the event telling it to terminate
//
SetEvent( event );
//
// Wait for the thread to terminate
//
if( WaitForSingleObject( threadHandle, INFINITE ) == WAIT_FAILED ) {
error = GetLastError();
}
//
// Close the handles
//
CloseHandle( event );
CloseHandle( threadHandle );
}
if ( error != NO_ERROR ) {
IF_DEBUG(INITIALIZATION_ERRORS) {
SS_PRINT(( "RecreateStickyShares: failed to add share "
"%ws = %wZ: %ld\n", ValueName, &pathString, error ));
}
}
//
// If this is a share which can be cached, set the caching flag in the server
//
si1005.shi1005_flags = CacheState;
if( si1005.shi1005_flags ) {
shareInfoBuffer.ShareInfo1005 = &si1005;
NetrShareSetInfo( NULL, ValueName, 1005, &shareInfoBuffer, NULL );
}
//
// free buffers allocated by GetStickyShareInfo
//
if ( remarkString.Buffer != NULL ) {
RtlFreeUnicodeString( &remarkString );
}
if ( pathString.Buffer != NULL ) {
RtlFreeUnicodeString( &pathString );
}
if ( shi502.shi502_security_descriptor != NULL ) {
MIDL_user_free( shi502.shi502_security_descriptor );
}
}
return NO_ERROR;
} // RecreateStickyShare
NTSTATUS
SaveSdToRegistry(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PWSTR ShareName
)
/*++
Routine Description:
Stores the share file security descriptor in the registry.
Arguments:
SecurityDescriptor - Points to a self-relative security descriptor
describing the access rights for files under this share.
ShareName - Points to a string containing the share name under
which the SD is to be stored.
Return Value:
Status of the operation.
--*/
{
NTSTATUS status;
//
// Store the security descriptor
//
ULONG fileSDLength;
if ( !RtlValidSecurityDescriptor( SecurityDescriptor ) ) {
status = STATUS_INVALID_SECURITY_DESCR;
} else {
fileSDLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
status = RtlWriteRegistryValue(
RTL_REGISTRY_SERVICES,
SHARES_SECURITY_REGISTRY_PATH,
ShareName,
REG_BINARY,
(LPBYTE)SecurityDescriptor,
fileSDLength
);
}
return status;
} // SaveSdToRegistry
NTSTATUS
SetSizeParameters (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
{
NT_PRODUCT_TYPE productType;
DWORD size;
LPWSTR subStrings[2];
ValueLength, Context, EntryContext;
//
// Get the product type.
//
if ( !RtlGetNtProductType( &productType ) ) {
productType = NtProductWinNt;
}
SsData.ServerInfo598.sv598_producttype = productType;
//
// Make sure that we got called for the right value.
//
ASSERT( _wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0 );
//
// The Size value must be a DWORD, and must be in the following
// range:
//
// 0 -> use defaults
// 1 -> small server (minimize memory usage)
// 2 -> medium server (balance)
// 3 -> large server (maximize connections)
//
if ( ValueType == REG_DWORD ) {
ASSERT( ValueLength == sizeof(DWORD) );
size = *(LPDWORD)ValueData;
}
if ( (ValueType != REG_DWORD) || (size > 3) ) {
subStrings[0] = ValueName;
subStrings[1] = PARAMETERS_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetSizeParameters: skipping invalid value "
"%ws\n", ValueName ));
}
return STATUS_SUCCESS;
}
SsData.ServerInfo598.sv598_serversize = size;
//
// Set appropriate fields based on the product type (Windows NT or
// Advanced Server) and the selected Size. Note that a Size of 0
// doesn't change any of the defaults.
//
// Note that the user limit is always -1 (unlimited). Autodisconnect
// always defaults to 15 minutes.
//
if ( size != 0 ) {
SYSTEM_BASIC_INFORMATION basicInfo;
NTSTATUS status;
ULONG noOfMb;
ULONG factor;
ULONG asFactor;
//
// Get system memory size.
//
status = NtQuerySystemInformation(
SystemBasicInformation,
&basicInfo,
sizeof( SYSTEM_BASIC_INFORMATION ),
NULL
);
if ( status != STATUS_SUCCESS ) {
subStrings[0] = ValueName;
subStrings[1] = PARAMETERS_REGISTRY_PATH;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetSizeParameters: NtQuerySystemInfo failed %x\n",
status ));
}
return STATUS_SUCCESS;
}
//
// Note that we first divide the page size by 512 in order to
// allow for physical memory sizes above 2^32-1. With this
// calculation, we can handle up to two terabytes of physical
// memory. The calculation assumes that the page size is at
// least 512, and is not very accurate if the page size is not
// a power of 2 (very unlikely).
//
ASSERT( basicInfo.PageSize >= 512 );
noOfMb = (((basicInfo.PageSize / 512) *
basicInfo.NumberOfPhysicalPages) +
(1 MB / 512 - 1)) / (1 MB / 512);
//
// Minimum is 8 MB
//
noOfMb = MAX( MIN_SYSTEM_SIZE, noOfMb );
//
// If we have NTAS, and we're set to maximize performance or we have
// lots of memory -- then set the default work item buffer size to
// a larger value. This value has been chosen to work well with our
// implementation of TCP/IP, and shows itself to advantage when doing
// directory emumerations with directories having lots of entries in them.
//
if( productType != NtProductWinNt && ((noOfMb >= 512) && (size == 3)) ) {
SsData.ServerInfo599.sv599_sizreqbuf = DEF_LARGE_SIZREQBUF;
}
//
// Set the maximum for the different sizes
//
if ( size == 1 ) {
noOfMb = MIN( noOfMb, MAX_SMALL_SIZE );
} else if ( size == 2 ) {
noOfMb = MIN( noOfMb, MAX_MEDIUM_SIZE );
}
//
// If small, assume the system size is half of the real one.
// This should give us half the paramater values of a medium server.
// If large, double it. Also set the free connection count.
//
if ( size == 1 ) {
//
// Small
//
factor = (noOfMb + 1) / 2;
SsData.ServerInfo599.sv599_minfreeconnections = 2;
SsData.ServerInfo599.sv599_maxfreeconnections = 2;
} else if ( size == 2 ) {
//
// Balanced
//
factor = noOfMb;
SsData.ServerInfo599.sv599_minfreeconnections = 2;
SsData.ServerInfo599.sv599_maxfreeconnections = 4;
} else {
//
// Large
//
factor = noOfMb * 2;
// Scale up our big servers, this uses the NEW version of small/med/large we picked
// for the server service (< 1 GB, 1-16 GB, >16 GB)
if( noOfMb < 1024 )
{
SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_SMALL;
SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_SMALL;
}
else if( noOfMb < 16*1024 )
{
// >= 1 GB memory
SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_MEDIUM;
SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_MEDIUM;
}
else {
// >= 16 GB memory
SsData.ServerInfo599.sv599_minfreeconnections = SRV_MIN_CONNECTIONS_LARGE;
SsData.ServerInfo599.sv599_maxfreeconnections = SRV_MAX_CONNECTIONS_LARGE;
}
}
//
// If this is an Advanced Server with at least 24M, some
// parameter will need to be even bigger.
//
asFactor = 1;
if ( (productType != NtProductWinNt) && (noOfMb >= 24) ) asFactor = 2;
//
// Now set the values for a medium server with this much memory.
//
SsData.ServerInfo599.sv599_maxworkitems =
MedSrvCfgTbl.maxworkitems[0] * factor * asFactor /
MedSrvCfgTbl.maxworkitems[1];
SsData.ServerInfo599.sv599_initworkitems =
MedSrvCfgTbl.initworkitems[0] * factor * asFactor /
MedSrvCfgTbl.initworkitems[1];
SsData.ServerInfo599.sv599_rawworkitems =
MedSrvCfgTbl.rawworkitems[0] * factor /
MedSrvCfgTbl.rawworkitems[1];
SsData.ServerInfo598.sv598_maxrawworkitems =
MedSrvCfgTbl.maxrawworkitems[0] * factor * asFactor /
MedSrvCfgTbl.maxrawworkitems[1];
SsData.ServerInfo599.sv599_maxworkitems =
MIN( SsData.ServerInfo599.sv599_maxworkitems, MAX_MAXWORKITEMS );
SsData.ServerInfo599.sv599_initworkitems =
MIN( SsData.ServerInfo599.sv599_initworkitems, MAX_INITWORKITEMS/4 );
SsData.ServerInfo599.sv599_rawworkitems =
MIN( SsData.ServerInfo599.sv599_rawworkitems, MAX_RAWWORKITEMS/4 );
SsData.ServerInfo598.sv598_maxrawworkitems =
MIN( SsData.ServerInfo598.sv598_maxrawworkitems, MAX_MAXRAWWORKITEMS );
if ( (productType != NtProductWinNt) || (size == 3) ) {
SsData.ServerInfo599.sv599_maxpagedmemoryusage = INF;
SsData.ServerInfo599.sv599_maxnonpagedmemoryusage = INF;
} else {
SsData.ServerInfo599.sv599_maxpagedmemoryusage =
MedSrvCfgTbl.maxpagedmemoryusage[0] * factor /
MedSrvCfgTbl.maxpagedmemoryusage[1] MB;
SsData.ServerInfo599.sv599_maxpagedmemoryusage =
MAX( SsData.ServerInfo599.sv599_maxpagedmemoryusage,
MIN_MAXPAGEDMEMORYUSAGE);
SsData.ServerInfo599.sv599_maxnonpagedmemoryusage =
MedSrvCfgTbl.maxnonpagedmemoryusage[0] * factor /
MedSrvCfgTbl.maxnonpagedmemoryusage[1] MB;
SsData.ServerInfo599.sv599_maxnonpagedmemoryusage =
MAX( SsData.ServerInfo599.sv599_maxnonpagedmemoryusage,
MIN_MAXNONPAGEDMEMORYUSAGE);
}
}
return STATUS_SUCCESS;
} // SetSizeParameters
NTSTATUS
SetStickyParameter (
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
{
NET_API_STATUS error;
DWORD_PTR i;
PFIELD_DESCRIPTOR foundField = NULL;
LPWSTR subStrings[2];
ValueLength, EntryContext;
//
// Ignore several parameters, since they are handled elsewhere
//
if( (_wcsicmp( ValueName, SIZE_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, NULL_SESSION_SHARES_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, NULL_SESSION_PIPES_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, PIPES_NEED_LICENSE_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, ERROR_LOG_IGNORE_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, GUID_VARIABLE_NAME ) == 0) ||
(_wcsicmp( ValueName, OPTIONAL_NAMES_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, NO_REMAP_PIPES_VALUE_NAME ) == 0) ||
(_wcsicmp( ValueName, SERVICE_DLL_VALUE_NAME ) == 0) ) {
return STATUS_SUCCESS;
}
//
// Determine which field we need to set, based on the value
// name.
//
// NOTE: For Daytona, disc and comment are now invalid registry names.
// We use their more famous aliases autodisconnect and srvcomment
// instead. If we get more of these cases, we should consider adding
// a field to the FIELD_DESCRIPTOR structure that indicates whether
// the names are should appear on the registry or not. Any change
// here should also be made to SsSetField().
//
if ( (_wcsicmp( ValueName, DISC_VALUE_NAME ) != 0) &&
(_wcsicmp( ValueName, COMMENT_VALUE_NAME ) != 0) ) {
for ( i = 0;
SsServerInfoFields[i].FieldName != NULL;
i++ ) {
if ( _wcsicmp( ValueName, SsServerInfoFields[i].FieldName ) == 0 ) {
foundField = &SsServerInfoFields[i];
break;
}
}
}
if ( foundField == NULL || foundField->Settable == NOT_SETTABLE ) {
#ifdef DBG
subStrings[0] = ValueName;
subStrings[1] = Context;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetStickyParameter: ignoring %s \"%ws\"\n",
(foundField == NULL ? "unknown value name" :
"unsettable value"), ValueName ));
}
#endif
return STATUS_SUCCESS;
}
switch ( foundField->FieldType ) {
case BOOLEAN_FIELD:
case DWORD_FIELD:
if ( ValueType != REG_DWORD ) {
subStrings[0] = ValueName;
subStrings[1] = Context;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetStickyParameter: skipping invalid value "
"%ws\n", ValueName ));
}
return STATUS_SUCCESS;
}
i = *(LPDWORD)ValueData;
break;
case LPSTR_FIELD:
if ( ValueType != REG_SZ ) {
subStrings[0] = ValueName;
subStrings[1] = Context;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
NO_ERROR
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetStickyParameter: skipping invalid value "
"%ws\n", ValueName ));
}
return STATUS_SUCCESS;
}
if (ValueLength != 0) {
i = (DWORD_PTR)ValueData;
} else {
i = (DWORD_PTR)NULL;
}
break;
}
//
// Set the field.
//
error = SsSetField( foundField, &i, FALSE, NULL );
#ifdef DBG
if ( error != NO_ERROR ) {
subStrings[0] = ValueName;
subStrings[1] = Context;
SsLogEvent(
EVENT_SRV_INVALID_REGISTRY_VALUE,
2,
subStrings,
error
);
IF_DEBUG(REGISTRY) {
SS_PRINT(( "SetStickyParameter: error %ld ignored in setting "
"parameter \"%ws\"n", error, ValueName ));
}
}
#endif
return STATUS_SUCCESS;
} // SetStickyParameter