2992 lines
75 KiB
C
2992 lines
75 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|