1408 lines
38 KiB
C
1408 lines
38 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
wksta.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the worker routines for the NetWksta APIs
|
||
implemented in the Workstation service.
|
||
|
||
Author:
|
||
|
||
Rita Wong (ritaw) 20-Feb-1991
|
||
|
||
Revision History:
|
||
|
||
14-May-1992 JohnRo
|
||
Implemented "sticky set info" and registry watch.
|
||
Corrected an info level: 1015 should be 1013.
|
||
--*/
|
||
|
||
#include "wsutil.h"
|
||
#include "wsdevice.h"
|
||
#include "wssec.h"
|
||
#include "wslsa.h"
|
||
#include "wsconfig.h"
|
||
#include "wswksta.h"
|
||
#include <config.h>
|
||
#include <confname.h>
|
||
#include <prefix.h>
|
||
#include "wsregcfg.h" // Registry helpers
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local function prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsValidateAndSetWksta(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer,
|
||
OUT LPDWORD ErrorParameter OPTIONAL,
|
||
OUT LPDWORD Parmnum
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetSystemInfo(
|
||
IN DWORD Level,
|
||
OUT LPBYTE *BufferPointer
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetPlatformInfo(
|
||
IN DWORD Level,
|
||
OUT LPBYTE *BufferPointer
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsFillSystemBufferInfo(
|
||
IN DWORD Level,
|
||
IN DWORD NumberOfLoggedOnUsers,
|
||
OUT LPBYTE *OutputBuffer
|
||
);
|
||
|
||
STATIC
|
||
VOID
|
||
WsUpdateRegistryToMatchWksta(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
);
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaGetInfo(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN DWORD Level,
|
||
OUT LPWKSTA_INFO WkstaInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaGetInfo entry point in the Workstation
|
||
service. It checks the security access of the caller before returning
|
||
one the information requested which is either system-wide, or redirector
|
||
specific.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
WkstaInfo - Returns a pointer to a buffer which contains the requested
|
||
workstation information.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status = NERR_Success;
|
||
LPBYTE Buffer;
|
||
|
||
ACCESS_MASK DesiredAccess;
|
||
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Determine the desired access based on the specified info level.
|
||
//
|
||
switch (Level) {
|
||
|
||
//
|
||
// Guest access
|
||
//
|
||
case 100:
|
||
DesiredAccess = WKSTA_CONFIG_GUEST_INFO_GET;
|
||
break;
|
||
|
||
//
|
||
// User access
|
||
//
|
||
case 101:
|
||
DesiredAccess = WKSTA_CONFIG_USER_INFO_GET;
|
||
break;
|
||
|
||
//
|
||
// Admin or operator access
|
||
//
|
||
case 102:
|
||
case 502:
|
||
DesiredAccess = WKSTA_CONFIG_ADMIN_INFO_GET;
|
||
break;
|
||
|
||
default:
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
//
|
||
// Perform access validation on the caller.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
DesiredAccess, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Only allowed to proceed with get info if no one else is setting
|
||
//
|
||
if (! RtlAcquireResourceShared(&WsInfo.ConfigResource, TRUE)) {
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
try {
|
||
switch (Level) {
|
||
|
||
//
|
||
// System-wide information
|
||
//
|
||
case 100:
|
||
case 101:
|
||
case 102:
|
||
status = WsGetSystemInfo(Level, &Buffer);
|
||
if (status == NERR_Success) {
|
||
SET_SYSTEM_INFO_POINTER(WkstaInfo, Buffer);
|
||
}
|
||
break;
|
||
|
||
//
|
||
// Platform specific information
|
||
//
|
||
case 502:
|
||
status = WsGetPlatformInfo(
|
||
Level,
|
||
(LPBYTE *) &(WkstaInfo->WkstaInfo502)
|
||
);
|
||
break;
|
||
|
||
//
|
||
// This should have been caught earlier.
|
||
//
|
||
default:
|
||
NetpAssert(FALSE);
|
||
status = ERROR_INVALID_LEVEL;
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
RtlReleaseResource(&WsInfo.ConfigResource);
|
||
return RtlNtStatusToDosError(GetExceptionCode());
|
||
}
|
||
|
||
RtlReleaseResource(&WsInfo.ConfigResource);
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaSetInfo(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN DWORD Level,
|
||
IN LPWKSTA_INFO WkstaInfo,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaSetInfo entry point in the Workstation
|
||
service. It checks the security access of the caller to make sure
|
||
that the caller is allowed to set specific workstation information.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
Level - Supplies the level of information.
|
||
|
||
WkstaInfo - Supplies a pointer to union structure of pointers to
|
||
buffer of fields to set. The level denotes the fields supplied in
|
||
this buffer.
|
||
|
||
ErrorParameter - Returns the identifier to the invalid parameter if
|
||
this function returns ERROR_INVALID_PARAMETER.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
WKSTA_INFO_502 OriginalWksta = WSBUF;
|
||
NET_API_STATUS status = NERR_Success;
|
||
DWORD Parmnum;
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Check for NULL input buffer
|
||
//
|
||
if (WkstaInfo->WkstaInfo502 == NULL) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
|
||
}
|
||
|
||
//
|
||
// Only admins can set redirector configurable fields. Validate access.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
WKSTA_CONFIG_INFO_SET, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// Serialize write access
|
||
//
|
||
if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
status = WsValidateAndSetWksta(
|
||
Level,
|
||
(LPBYTE) WkstaInfo->WkstaInfo502,
|
||
ErrorParameter,
|
||
&Parmnum
|
||
);
|
||
|
||
if (status != NERR_Success) {
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Set NT redirector specific fields
|
||
//
|
||
status = WsUpdateRedirToMatchWksta(
|
||
Parmnum,
|
||
ErrorParameter
|
||
);
|
||
|
||
if (status != NERR_Success) {
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Make updates "sticky" (update registry to match wksta).
|
||
//
|
||
WsUpdateRegistryToMatchWksta(
|
||
Level,
|
||
(LPBYTE) WkstaInfo->WkstaInfo502,
|
||
ErrorParameter
|
||
);
|
||
|
||
CleanExit:
|
||
if (status != NERR_Success) {
|
||
WSBUF = OriginalWksta;
|
||
}
|
||
RtlReleaseResource(&WsInfo.ConfigResource);
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaTransportEnum(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN OUT LPWKSTA_TRANSPORT_ENUM_STRUCT TransportInfo,
|
||
IN DWORD PreferedMaximumLength,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaTransportEnum entry point in the
|
||
Workstation service.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
TransportInfo - This structure supplies the level of information requested,
|
||
returns a pointer to the buffer allocated by the Workstation service
|
||
which contains a sequence of information structure of the specified
|
||
information level, and returns the number of entries read. The buffer
|
||
pointer is set to NULL if return code is not NERR_Success or
|
||
ERROR_MORE_DATA, or if EntriesRead returned is 0. The EntriesRead
|
||
value is only valid if the return code is NERR_Success or
|
||
ERROR_MORE_DATA.
|
||
|
||
PreferedMaximumLength - Supplies the number of bytes of information
|
||
to return in the buffer. If this value is MAXULONG, all available
|
||
information will be returned.
|
||
|
||
TotalEntries - Returns the total number of entries available. This value
|
||
is only valid if the return code is NERR_Success or ERROR_MORE_DATA.
|
||
|
||
ResumeHandle - Supplies a handle to resume the enumeration from where it
|
||
left off the last time through. Returns the resume handle if return
|
||
code is ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
LMR_REQUEST_PACKET Rrp; // Redirector request packet
|
||
DWORD EnumTransportHintSize = 0; // Hint size from redirector
|
||
|
||
LPBYTE Buffer;
|
||
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Only level 0 is valid
|
||
//
|
||
if (TransportInfo->Level != 0) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
//
|
||
// Set up request packet. Output buffer structure is of enumerate
|
||
// transport type.
|
||
//
|
||
Rrp.Version = REQUEST_PACKET_VERSION;
|
||
Rrp.Type = EnumerateTransports;
|
||
Rrp.Level = TransportInfo->Level;
|
||
Rrp.Parameters.Get.ResumeHandle = (ARGUMENT_PRESENT(ResumeHandle)) ?
|
||
*ResumeHandle : 0;
|
||
|
||
//
|
||
// Get the requested information from the redirector.
|
||
//
|
||
status = WsDeviceControlGetInfo(
|
||
Redirector,
|
||
WsRedirDeviceHandle,
|
||
FSCTL_LMR_ENUMERATE_TRANSPORTS,
|
||
&Rrp,
|
||
sizeof(LMR_REQUEST_PACKET),
|
||
&Buffer,
|
||
PreferedMaximumLength,
|
||
EnumTransportHintSize,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Return output parameters
|
||
//
|
||
if (status == NERR_Success || status == ERROR_MORE_DATA) {
|
||
SET_TRANSPORT_ENUM_POINTER(
|
||
TransportInfo,
|
||
Buffer,
|
||
Rrp.Parameters.Get.EntriesRead
|
||
);
|
||
|
||
if (TransportInfo->WkstaTransportInfo.Level0 == NULL)
|
||
{
|
||
LocalFree(Buffer);
|
||
}
|
||
|
||
*TotalEntries = Rrp.Parameters.Get.TotalEntries;
|
||
|
||
if (status == ERROR_MORE_DATA && ARGUMENT_PRESENT(ResumeHandle)) {
|
||
*ResumeHandle = Rrp.Parameters.Get.ResumeHandle;
|
||
}
|
||
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaTransportAdd (
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN DWORD Level,
|
||
IN LPWKSTA_TRANSPORT_INFO_0 TransportInfo,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaTransportAdd entry point in the
|
||
Workstation service.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
TransportInfo - Supplies the information structure to add a new transport.
|
||
|
||
ErrorParameter - Returns the identifier to the invalid parameter if this
|
||
function returns ERROR_INVALID_PARAMETER.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Only admins can add a transport. Validate access.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
WKSTA_CONFIG_INFO_SET, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
|
||
//
|
||
// 0 is the only valid level
|
||
//
|
||
if (Level != 0) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
if (TransportInfo->wkti0_transport_name == NULL) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, TRANSPORT_NAME_PARMNUM);
|
||
}
|
||
|
||
return WsBindTransport(
|
||
TransportInfo->wkti0_transport_name,
|
||
TransportInfo->wkti0_quality_of_service,
|
||
ErrorParameter
|
||
);
|
||
}
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrWkstaTransportDel (
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN LPTSTR TransportName,
|
||
IN DWORD ForceLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetWkstaTransportDel entry point in the
|
||
Workstation service.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
TransportName - Supplies the name of the transport to delete.
|
||
|
||
ForceLevel - Supplies the level of force to delete the tree connections on
|
||
the transport we are unbinding from.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Only admins can delete a transport. Validate access.
|
||
//
|
||
if (NetpAccessCheckAndAudit(
|
||
WORKSTATION_DISPLAY_NAME, // Subsystem name
|
||
(LPTSTR) CONFIG_INFO_OBJECT, // Object type name
|
||
ConfigurationInfoSd, // Security descriptor
|
||
WKSTA_CONFIG_INFO_SET, // Desired access
|
||
&WsConfigInfoMapping // Generic mapping
|
||
) != NERR_Success) {
|
||
|
||
return ERROR_ACCESS_DENIED;
|
||
}
|
||
|
||
if (TransportName == NULL) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Check that ForceLevel parameter is valid, which the redirector and
|
||
// browser use to delete the connections on the transport we are
|
||
// unbinding from.
|
||
//
|
||
switch (ForceLevel) {
|
||
|
||
case USE_FORCE:
|
||
ForceLevel = USE_NOFORCE;
|
||
break;
|
||
|
||
case USE_NOFORCE:
|
||
case USE_LOTS_OF_FORCE:
|
||
break;
|
||
|
||
default:
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
return WsUnbindTransport(TransportName, ForceLevel);
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetSystemInfo(
|
||
IN DWORD Level,
|
||
OUT LPBYTE *BufferPointer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the Redirector FSD, the LSA subsystem and the
|
||
MSV1_0 authentication package, and the Datagram Receiver DD to get
|
||
the system wide information returned by NetWkstaGetInfo API.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
BufferPointer - Returns a pointer to a buffer which contains the
|
||
requested workstation information.
|
||
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
DWORD NumberOfLoggedOnUsers = 1;
|
||
|
||
//
|
||
// Get number of logged on users from the MSV1_0 authentication package
|
||
// if Level == 102.
|
||
//
|
||
if (Level == 102) {
|
||
|
||
PMSV1_0_ENUMUSERS_RESPONSE EnumUsersResponse = NULL;
|
||
|
||
//
|
||
// Ask authentication package to enumerate users who are physically
|
||
// logged to the local machine.
|
||
//
|
||
if ((status = WsLsaEnumUsers(
|
||
(LPBYTE *) &EnumUsersResponse
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
if (EnumUsersResponse == NULL) {
|
||
return ERROR_GEN_FAILURE;
|
||
}
|
||
|
||
NumberOfLoggedOnUsers = EnumUsersResponse->NumberOfLoggedOnUsers;
|
||
|
||
(VOID) LsaFreeReturnBuffer(EnumUsersResponse);
|
||
}
|
||
|
||
//
|
||
// Put all the data collected into output buffer allocated by this routine.
|
||
//
|
||
return WsFillSystemBufferInfo(
|
||
Level,
|
||
NumberOfLoggedOnUsers,
|
||
BufferPointer
|
||
);
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetPlatformInfo(
|
||
IN DWORD Level,
|
||
OUT LPBYTE *BufferPointer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the Redirector FSD to get the Redirector platform
|
||
specific information returned by NetWkstaGetInfo API.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
BufferPointer - Returns the pointer a buffer which contains the requested
|
||
redirector specific information.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
LMR_REQUEST_PACKET Rrp; // Redirector request packet
|
||
PWKSTA_INFO_502 Info;
|
||
|
||
//
|
||
// There is only one redirector info level: 502.
|
||
//
|
||
NetpAssert(Level == 502);
|
||
|
||
//
|
||
// Set up request packet. Output buffer structure is of configuration
|
||
// information type.
|
||
//
|
||
Rrp.Version = REQUEST_PACKET_VERSION;
|
||
Rrp.Level = Level;
|
||
Rrp.Type = ConfigInformation;
|
||
|
||
//
|
||
// Get the requested information from the Redirector. This routine
|
||
// allocates the returned buffer.
|
||
//
|
||
status = WsDeviceControlGetInfo(
|
||
Redirector,
|
||
WsRedirDeviceHandle,
|
||
FSCTL_LMR_GET_CONFIG_INFO,
|
||
&Rrp,
|
||
sizeof(LMR_REQUEST_PACKET),
|
||
BufferPointer,
|
||
sizeof(WKSTA_INFO_502),
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if (status == NERR_Success) {
|
||
Info = (PWKSTA_INFO_502) *BufferPointer;
|
||
|
||
//
|
||
// Fill datagram receiver fields in level 502 structure from global
|
||
// Workstation buffer (WSBUF). There are no FSCtl APIs to get or
|
||
// set them in the datagram receiver.
|
||
//
|
||
Info->wki502_num_mailslot_buffers =
|
||
WSBUF.wki502_num_mailslot_buffers;
|
||
Info->wki502_num_srv_announce_buffers =
|
||
WSBUF.wki502_num_srv_announce_buffers;
|
||
Info->wki502_max_illegal_datagram_events =
|
||
WSBUF.wki502_max_illegal_datagram_events;
|
||
Info->wki502_illegal_datagram_event_reset_frequency =
|
||
WSBUF.wki502_illegal_datagram_event_reset_frequency;
|
||
Info->wki502_log_election_packets =
|
||
WSBUF.wki502_log_election_packets;;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS
|
||
WsValidateAndSetWksta(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer,
|
||
OUT LPDWORD ErrorParameter OPTIONAL,
|
||
OUT LPDWORD Parmnum
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the user specified config fields into the global
|
||
WsInfo.WsConfigBuf (WSBUF) buffer and validates that the fields
|
||
are valid.
|
||
|
||
It returns the associated parmnum value so that the caller can
|
||
specify it to the redirector.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the requested level of information.
|
||
|
||
Buffer - Supplies a buffer which contains the user specified config
|
||
fields.
|
||
|
||
ErrorParameter - Receives the parmnum value of the field that is
|
||
invalid if ERROR_INVALID_PARAMETER is returned.
|
||
|
||
Parmnum - Receives the parmnum for the field(s) being set.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD i;
|
||
|
||
//
|
||
// Perform range checking
|
||
//
|
||
|
||
switch (Level) {
|
||
|
||
case 502: // Set all fields
|
||
WSBUF = *((PWKSTA_INFO_502) Buffer);
|
||
*Parmnum = PARMNUM_ALL;
|
||
break;
|
||
|
||
case 1010: // char_wait
|
||
WSBUF.wki502_char_wait = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_CHARWAIT_PARMNUM;
|
||
break;
|
||
|
||
case 1011: // collection_time
|
||
WSBUF.wki502_collection_time = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_CHARTIME_PARMNUM;
|
||
break;
|
||
|
||
case 1012: // maximum_collection_count
|
||
WSBUF.wki502_maximum_collection_count = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_CHARCOUNT_PARMNUM;
|
||
break;
|
||
|
||
case 1013: // keep_conn
|
||
WSBUF.wki502_keep_conn = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_KEEPCONN_PARMNUM;
|
||
break;
|
||
|
||
case 1018: // sess_timeout
|
||
WSBUF.wki502_sess_timeout = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_SESSTIMEOUT_PARMNUM;
|
||
break;
|
||
|
||
case 1023: // siz_char_buf
|
||
WSBUF.wki502_siz_char_buf = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_SIZCHARBUF_PARMNUM;
|
||
break;
|
||
|
||
case 1033: // max_threads
|
||
WSBUF.wki502_max_threads = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_MAXTHREADS_PARMNUM;
|
||
break;
|
||
|
||
case 1041: // lock_quota
|
||
WSBUF.wki502_lock_quota = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_LOCKQUOTA_PARMNUM;
|
||
break;
|
||
|
||
case 1042: // lock_increment
|
||
WSBUF.wki502_lock_increment = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_LOCKINCREMENT_PARMNUM;
|
||
break;
|
||
|
||
case 1043: // lock_maximum
|
||
WSBUF.wki502_lock_maximum = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_LOCKMAXIMUM_PARMNUM;
|
||
break;
|
||
|
||
case 1044: // pipe_increment
|
||
WSBUF.wki502_pipe_increment = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_PIPEINCREMENT_PARMNUM;
|
||
break;
|
||
|
||
case 1045: // pipe_maximum
|
||
WSBUF.wki502_pipe_maximum = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_PIPEMAXIMUM_PARMNUM;
|
||
break;
|
||
|
||
case 1046: // dormant_file_limit
|
||
WSBUF.wki502_dormant_file_limit = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_DORMANTFILELIMIT_PARMNUM;
|
||
break;
|
||
|
||
case 1047: // cache_file_timeout
|
||
WSBUF.wki502_cache_file_timeout = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_CACHEFILETIMEOUT_PARMNUM;
|
||
break;
|
||
|
||
case 1048: // use_opportunistic_locking
|
||
WSBUF.wki502_use_opportunistic_locking = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM;
|
||
break;
|
||
|
||
case 1049: // use_unlock_behind
|
||
WSBUF.wki502_use_unlock_behind = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USEUNLOCKBEHIND_PARMNUM;
|
||
break;
|
||
|
||
case 1050: // use_close_behind
|
||
WSBUF.wki502_use_close_behind = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USECLOSEBEHIND_PARMNUM;
|
||
break;
|
||
|
||
case 1051: // buf_named_pipes
|
||
WSBUF.wki502_buf_named_pipes = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_BUFFERNAMEDPIPES_PARMNUM;
|
||
break;
|
||
|
||
case 1052: // use_lock_read_unlock
|
||
WSBUF.wki502_use_lock_read_unlock = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM;
|
||
break;
|
||
|
||
case 1053: // utilize_nt_caching
|
||
WSBUF.wki502_utilize_nt_caching = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_UTILIZENTCACHING_PARMNUM;
|
||
break;
|
||
|
||
case 1054: // use_raw_read
|
||
WSBUF.wki502_use_raw_read = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USERAWREAD_PARMNUM;
|
||
break;
|
||
|
||
case 1055: // use_raw_write
|
||
WSBUF.wki502_use_raw_write = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USERAWWRITE_PARMNUM;
|
||
break;
|
||
|
||
case 1056: // use_write_raw_data
|
||
WSBUF.wki502_use_write_raw_data = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USEWRITERAWWITHDATA_PARMNUM;
|
||
break;
|
||
|
||
case 1057: // use_encryption
|
||
WSBUF.wki502_use_encryption = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USEENCRYPTION_PARMNUM;
|
||
break;
|
||
|
||
case 1058: // buf_files_deny_write
|
||
WSBUF.wki502_buf_files_deny_write = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_BUFFILESWITHDENYWRITE_PARMNUM;
|
||
break;
|
||
|
||
case 1059: // buf_read_only_files
|
||
WSBUF.wki502_buf_read_only_files = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_BUFFERREADONLYFILES_PARMNUM;
|
||
break;
|
||
|
||
case 1060: // force_core_create_mode
|
||
WSBUF.wki502_force_core_create_mode = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_FORCECORECREATEMODE_PARMNUM;
|
||
break;
|
||
|
||
case 1061: // use_512_byte_max_transfer
|
||
WSBUF.wki502_use_512_byte_max_transfer = *((LPBOOL) Buffer);
|
||
*Parmnum = WKSTA_USE512BYTESMAXTRANSFER_PARMNUM;
|
||
break;
|
||
|
||
case 1062: // read_ahead_throughput
|
||
WSBUF.wki502_read_ahead_throughput = *((LPDWORD) Buffer);
|
||
*Parmnum = WKSTA_READAHEADTHRUPUT_PARMNUM;
|
||
break;
|
||
|
||
default:
|
||
if (ErrorParameter != NULL) {
|
||
*ErrorParameter = PARM_ERROR_UNKNOWN;
|
||
}
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
|
||
for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
|
||
|
||
//
|
||
// Check the range of all fields. If any fail, we return
|
||
// ERROR_INVALID_PARAMETER.
|
||
//
|
||
if (((WsInfo.WsConfigFields[i].DataType == DWordType) &&
|
||
(*(WsInfo.WsConfigFields[i].FieldPtr) <
|
||
WsInfo.WsConfigFields[i].Minimum ||
|
||
*(WsInfo.WsConfigFields[i].FieldPtr) >
|
||
WsInfo.WsConfigFields[i].Maximum))
|
||
||
|
||
((WsInfo.WsConfigFields[i].DataType == BooleanType) &&
|
||
(*(WsInfo.WsConfigFields[i].FieldPtr) != TRUE &&
|
||
*(WsInfo.WsConfigFields[i].FieldPtr) != FALSE))) {
|
||
|
||
//
|
||
// We have a problem if this is not a field we want
|
||
// to set, and we still happen to find a bad value.
|
||
//
|
||
NetpAssert((*Parmnum == PARMNUM_ALL) ||
|
||
(*Parmnum == WsInfo.WsConfigFields[i].Parmnum));
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
PREFIX_WKSTA "Parameter %s has bad value %u, parmnum %u\n",
|
||
WsInfo.WsConfigFields[i].Keyword,
|
||
*(WsInfo.WsConfigFields[i].FieldPtr),
|
||
WsInfo.WsConfigFields[i].Parmnum
|
||
));
|
||
}
|
||
|
||
RETURN_INVALID_PARAMETER(
|
||
ErrorParameter,
|
||
WsInfo.WsConfigFields[i].Parmnum
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
return NERR_Success;
|
||
|
||
}
|
||
|
||
|
||
NET_API_STATUS
|
||
WsUpdateRedirToMatchWksta(
|
||
IN DWORD Parmnum,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the redirector to set the redirector platform specific
|
||
information with the values found in the global WsInfo.WsConfigBuf (WSBUF)
|
||
buffer.
|
||
|
||
Arguments:
|
||
|
||
Parmnum - Supplies the parameter number of the field if a single field
|
||
is being set. If all fields are being set, this value is PARMNUM_ALL.
|
||
|
||
|
||
ErrorParameter - Returns the identifier to the invalid parameter in Buffer
|
||
if this function returns ERROR_INVALID_PARAMETER.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS ApiStatus;
|
||
LMR_REQUEST_PACKET Rrp; // Redirector request packet
|
||
|
||
|
||
//
|
||
// Set up request packet. Input buffer structure is of configuration
|
||
// information type.
|
||
//
|
||
Rrp.Version = REQUEST_PACKET_VERSION;
|
||
Rrp.Level = 502;
|
||
Rrp.Type = ConfigInformation;
|
||
Rrp.Parameters.Set.WkstaParameter = Parmnum;
|
||
|
||
//
|
||
// Set the information in the Redirector.
|
||
//
|
||
ApiStatus = WsRedirFsControl(
|
||
WsRedirDeviceHandle,
|
||
FSCTL_LMR_SET_CONFIG_INFO,
|
||
&Rrp,
|
||
sizeof(LMR_REQUEST_PACKET),
|
||
(PVOID) &WSBUF,
|
||
sizeof(WSBUF),
|
||
NULL
|
||
);
|
||
|
||
if (ApiStatus == ERROR_INVALID_PARAMETER && ARGUMENT_PRESENT(ErrorParameter)) {
|
||
|
||
IF_DEBUG(INFO) {
|
||
NetpKdPrint((
|
||
PREFIX_WKSTA "NetrWkstaSetInfo: invalid parameter is %lu\n",
|
||
Rrp.Parameters.Set.WkstaParameter));
|
||
}
|
||
|
||
*ErrorParameter = Rrp.Parameters.Set.WkstaParameter;
|
||
}
|
||
|
||
return ApiStatus;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
WsUpdateRegistryToMatchWksta(
|
||
IN DWORD Level,
|
||
IN LPBYTE Buffer,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the registry to update the platform specific
|
||
information in the registry.
|
||
|
||
If any write operation to the registry fails, there is nothing we can
|
||
do about it because chances are good that we will not be able to back
|
||
out the changes since that requires more writes to the registry. When
|
||
this happens, the discrepancy between the registry and the redirector
|
||
will be straightened out when next key change notify occurs.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information.
|
||
|
||
Buffer - Supplies a buffer which contains the information structure
|
||
to set.
|
||
|
||
ErrorParameter - Returns the identifier to the invalid parameter in Buffer
|
||
if this function returns ERROR_INVALID_PARAMETER.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS ApiStatus;
|
||
LPNET_CONFIG_HANDLE SectionHandle = NULL;
|
||
DWORD i;
|
||
|
||
|
||
//
|
||
// Open section of config data.
|
||
//
|
||
ApiStatus = NetpOpenConfigData(
|
||
&SectionHandle,
|
||
NULL, // Local server.
|
||
SECT_NT_WKSTA, // Section name.
|
||
FALSE // Don't want read-only access.
|
||
);
|
||
|
||
if (ApiStatus != NERR_Success) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Macro to update one value in the registry.
|
||
// Assumes that Buffer starts with the new value for this field.
|
||
//
|
||
|
||
#define WRITE_ONE_PARM_TO_REGISTRY( KeyNamePart, TypeFlag ) \
|
||
{ \
|
||
if (TypeFlag == BooleanType) { \
|
||
(void) WsSetConfigBool( \
|
||
SectionHandle, \
|
||
WKSTA_KEYWORD_ ## KeyNamePart, \
|
||
* ((LPBOOL) Buffer) ); \
|
||
} else { \
|
||
NetpAssert( TypeFlag == DWordType ); \
|
||
(void) WsSetConfigDword( \
|
||
SectionHandle, \
|
||
WKSTA_KEYWORD_ ## KeyNamePart, \
|
||
* ((LPDWORD) Buffer) ); \
|
||
} \
|
||
}
|
||
|
||
//
|
||
// Update field based on the info level.
|
||
//
|
||
|
||
switch (Level) {
|
||
|
||
case 502: // Set all fields
|
||
|
||
for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
|
||
//
|
||
// Write this field to the registry.
|
||
//
|
||
if (WsInfo.WsConfigFields[i].DataType == DWordType) {
|
||
|
||
(void) WsSetConfigDword(
|
||
SectionHandle,
|
||
WsInfo.WsConfigFields[i].Keyword,
|
||
* ((LPDWORD) WsInfo.WsConfigFields[i].FieldPtr)
|
||
);
|
||
|
||
} else {
|
||
|
||
NetpAssert(WsInfo.WsConfigFields[i].DataType == BooleanType);
|
||
|
||
(void) WsSetConfigBool(
|
||
SectionHandle,
|
||
WsInfo.WsConfigFields[i].Keyword,
|
||
* ((LPBOOL) WsInfo.WsConfigFields[i].FieldPtr)
|
||
);
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case 1010: // char_wait
|
||
WRITE_ONE_PARM_TO_REGISTRY( CHARWAIT, DWordType );
|
||
break;
|
||
|
||
case 1011: // collection_time
|
||
WRITE_ONE_PARM_TO_REGISTRY( COLLECTIONTIME, DWordType );
|
||
break;
|
||
|
||
case 1012: // maximum_collection_count
|
||
WRITE_ONE_PARM_TO_REGISTRY( MAXCOLLECTIONCOUNT, DWordType );
|
||
break;
|
||
|
||
case 1013: // keep_conn
|
||
WRITE_ONE_PARM_TO_REGISTRY( KEEPCONN, DWordType );
|
||
break;
|
||
|
||
case 1018: // sess_timeout
|
||
WRITE_ONE_PARM_TO_REGISTRY( SESSTIMEOUT, DWordType );
|
||
break;
|
||
|
||
case 1023: // siz_char_buf
|
||
WRITE_ONE_PARM_TO_REGISTRY( SIZCHARBUF, DWordType );
|
||
break;
|
||
|
||
case 1033: // max_threads
|
||
WRITE_ONE_PARM_TO_REGISTRY( MAXTHREADS, DWordType );
|
||
break;
|
||
|
||
case 1041: // lock_quota
|
||
WRITE_ONE_PARM_TO_REGISTRY( LOCKQUOTA, DWordType );
|
||
break;
|
||
|
||
case 1042: // lock_increment
|
||
WRITE_ONE_PARM_TO_REGISTRY( LOCKINCREMENT, DWordType );
|
||
break;
|
||
|
||
case 1043: // lock_maximum
|
||
WRITE_ONE_PARM_TO_REGISTRY( LOCKMAXIMUM, DWordType );
|
||
break;
|
||
|
||
case 1044: // pipe_increment
|
||
WRITE_ONE_PARM_TO_REGISTRY( PIPEINCREMENT, DWordType );
|
||
break;
|
||
|
||
case 1045: // pipe_maximum
|
||
WRITE_ONE_PARM_TO_REGISTRY( PIPEMAXIMUM, DWordType );
|
||
break;
|
||
|
||
case 1046: // dormant_file_limit
|
||
WRITE_ONE_PARM_TO_REGISTRY( DORMANTFILELIMIT, DWordType );
|
||
break;
|
||
|
||
case 1047: // cache_file_timeout
|
||
WRITE_ONE_PARM_TO_REGISTRY( CACHEFILETIMEOUT, DWordType );
|
||
break;
|
||
|
||
case 1048: // use_opportunistic_locking
|
||
WRITE_ONE_PARM_TO_REGISTRY( USEOPLOCKING, BooleanType );
|
||
break;
|
||
|
||
case 1049: // use_unlock_behind
|
||
WRITE_ONE_PARM_TO_REGISTRY( USEUNLOCKBEHIND, BooleanType );
|
||
break;
|
||
|
||
case 1050: // use_close_behind
|
||
WRITE_ONE_PARM_TO_REGISTRY( USECLOSEBEHIND, BooleanType );
|
||
break;
|
||
|
||
case 1051: // buf_named_pipes
|
||
WRITE_ONE_PARM_TO_REGISTRY( BUFNAMEDPIPES, BooleanType );
|
||
break;
|
||
|
||
case 1052: // use_lock_read_unlock
|
||
WRITE_ONE_PARM_TO_REGISTRY( USELOCKREADUNLOCK, BooleanType );
|
||
break;
|
||
|
||
case 1053: // utilize_nt_caching
|
||
WRITE_ONE_PARM_TO_REGISTRY( UTILIZENTCACHING, BooleanType );
|
||
break;
|
||
|
||
case 1054: // use_raw_read
|
||
WRITE_ONE_PARM_TO_REGISTRY( USERAWREAD, BooleanType );
|
||
break;
|
||
|
||
case 1055: // use_raw_write
|
||
WRITE_ONE_PARM_TO_REGISTRY( USERAWWRITE, BooleanType );
|
||
break;
|
||
|
||
case 1056: // use_write_raw_data
|
||
WRITE_ONE_PARM_TO_REGISTRY( USEWRITERAWDATA, BooleanType );
|
||
break;
|
||
|
||
case 1057: // use_encryption
|
||
WRITE_ONE_PARM_TO_REGISTRY( USEENCRYPTION, BooleanType );
|
||
break;
|
||
|
||
case 1058: // buf_files_deny_write
|
||
WRITE_ONE_PARM_TO_REGISTRY( BUFFILESDENYWRITE, BooleanType );
|
||
break;
|
||
|
||
case 1059: // buf_read_only_files
|
||
WRITE_ONE_PARM_TO_REGISTRY( BUFREADONLYFILES, BooleanType );
|
||
break;
|
||
|
||
case 1060: // force_core_create_mode
|
||
WRITE_ONE_PARM_TO_REGISTRY( FORCECORECREATE, BooleanType );
|
||
break;
|
||
|
||
case 1061: // use_512_byte_max_transfer
|
||
WRITE_ONE_PARM_TO_REGISTRY( USE512BYTEMAXTRANS, BooleanType );
|
||
break;
|
||
|
||
case 1062: // read_ahead_throughput
|
||
WRITE_ONE_PARM_TO_REGISTRY( READAHEADTHRUPUT, DWordType );
|
||
break;
|
||
|
||
|
||
default:
|
||
//
|
||
// This should never happen
|
||
//
|
||
NetpAssert(FALSE);
|
||
}
|
||
|
||
if (SectionHandle != NULL) {
|
||
(VOID) NetpCloseConfigData( SectionHandle );
|
||
}
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsFillSystemBufferInfo(
|
||
IN DWORD Level,
|
||
IN DWORD NumberOfLoggedOnUsers,
|
||
OUT LPBYTE *OutputBuffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calculates the exact length of the output buffer needed,
|
||
allocates that amount, and fill the output buffer with all the requested
|
||
system-wide workstation information.
|
||
|
||
NOTE: This function assumes that info structure level 102 is a superset
|
||
of info structure level 100 and 101, and that the offset to each
|
||
common field is exactly the same. This allows us to take
|
||
advantage of a switch statement without a break between the levels.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
NumberOfLoggedOnUsers - Supplies the number of users logged on
|
||
interactively.
|
||
|
||
OutputBuffer - Returns a pointer to the buffer allocated by this routine
|
||
which is filled with the requested system-wide workstation
|
||
information.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failing to allocate the
|
||
output buffer.
|
||
|
||
--*/
|
||
{
|
||
PWKSTA_INFO_102 WkstaSystemInfo;
|
||
|
||
LPBYTE FixedDataEnd;
|
||
LPTSTR EndOfVariableData;
|
||
|
||
DWORD SystemInfoFixedLength = SYSTEM_INFO_FIXED_LENGTH(Level);
|
||
DWORD TotalBytesNeeded = SystemInfoFixedLength +
|
||
(WsInfo.WsComputerNameLength +
|
||
WsInfo.WsPrimaryDomainNameLength +
|
||
3) * sizeof(TCHAR); // include NULL character
|
||
// for LAN root
|
||
|
||
|
||
if ((*OutputBuffer = MIDL_user_allocate(TotalBytesNeeded)) == NULL) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
RtlZeroMemory((PVOID) *OutputBuffer, TotalBytesNeeded);
|
||
|
||
WkstaSystemInfo = (PWKSTA_INFO_102) *OutputBuffer;
|
||
|
||
FixedDataEnd = (LPBYTE) ((DWORD_PTR) *OutputBuffer + SystemInfoFixedLength);
|
||
EndOfVariableData = (LPTSTR) ((DWORD_PTR) *OutputBuffer + TotalBytesNeeded);
|
||
|
||
//
|
||
// Put the data into the output buffer.
|
||
//
|
||
switch (Level) {
|
||
|
||
case 102:
|
||
|
||
WkstaSystemInfo->wki102_logged_on_users = NumberOfLoggedOnUsers;
|
||
|
||
case 101:
|
||
|
||
//
|
||
// LAN root is set to NULL on NT.
|
||
//
|
||
NetpCopyStringToBuffer(
|
||
NULL,
|
||
0,
|
||
FixedDataEnd,
|
||
&EndOfVariableData,
|
||
&WkstaSystemInfo->wki102_lanroot
|
||
);
|
||
|
||
case 100:
|
||
|
||
WkstaSystemInfo->wki102_platform_id = WsInfo.RedirectorPlatform;
|
||
WkstaSystemInfo->wki102_ver_major = WsInfo.MajorVersion;
|
||
WkstaSystemInfo->wki102_ver_minor = WsInfo.MinorVersion;
|
||
|
||
NetpCopyStringToBuffer(
|
||
WsInfo.WsComputerName,
|
||
WsInfo.WsComputerNameLength,
|
||
FixedDataEnd,
|
||
&EndOfVariableData,
|
||
&WkstaSystemInfo->wki102_computername
|
||
);
|
||
|
||
NetpCopyStringToBuffer(
|
||
WsInfo.WsPrimaryDomainName,
|
||
WsInfo.WsPrimaryDomainNameLength,
|
||
FixedDataEnd,
|
||
&EndOfVariableData,
|
||
&WkstaSystemInfo->wki102_langroup
|
||
);
|
||
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// This should never happen.
|
||
//
|
||
NetpKdPrint(("WsFillSystemBufferInfo: Invalid level %lu\n", Level));
|
||
NetpAssert(FALSE);
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|