1423 lines
40 KiB
C
1423 lines
40 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
usegenum.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the worker routines for the NetUseGetInfo and
|
||
NetUseEnum APIs implemented in the Workstation service.
|
||
|
||
Author:
|
||
|
||
Rita Wong (ritaw) 13-Mar-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "wsutil.h"
|
||
#include "wsdevice.h"
|
||
#include "wsuse.h"
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local function prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN HANDLE TreeConnection,
|
||
IN PUSE_ENTRY UseEntry,
|
||
OUT LPBYTE *OutputBuffer
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN PUSE_ENTRY UseList,
|
||
IN LPBYTE ImplicitList,
|
||
IN DWORD TotalImplicit,
|
||
OUT LPBYTE *OutputBuffer,
|
||
IN DWORD PreferedMaximumLength,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumCombinedUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN LPBYTE ImplicitList,
|
||
IN DWORD TotalImplicit,
|
||
IN PUSE_ENTRY UseList,
|
||
OUT LPBYTE OutputBuffer,
|
||
IN DWORD OutputBufferLength,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetRedirUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN HANDLE TreeConnection,
|
||
OUT LPBYTE *OutputBuffer
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetCombinedUseInfo(
|
||
IN DWORD Level,
|
||
IN DWORD UseFixedLength,
|
||
IN PUSE_ENTRY UseEntry,
|
||
IN PLMR_CONNECTION_INFO_2 UncEntry,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN OUT LPDWORD EntriesRead OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
BOOL
|
||
WsFillUseBuffer(
|
||
IN DWORD Level,
|
||
IN PUSE_ENTRY UseEntry,
|
||
IN PLMR_CONNECTION_INFO_2 UncEntry,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UseFixedLength
|
||
);
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Macros //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
#define SET_USE_INFO_POINTER(InfoStruct, ResultBuffer) \
|
||
InfoStruct->UseInfo2 = (PUSE_INFO_2) ResultBuffer;
|
||
|
||
#define SET_USE_ENUM_POINTER(InfoStruct, ResultBuffer, NumRead) \
|
||
{InfoStruct->UseInfo.Level2->Buffer = (PUSE_INFO_2) ResultBuffer;\
|
||
InfoStruct->UseInfo.Level2->EntriesRead = NumRead;}
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrUseGetInfo(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN LPTSTR UseName,
|
||
IN DWORD Level,
|
||
OUT LPUSE_INFO InfoStruct
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetUseGetInfo entry point in the Workstation service.
|
||
|
||
This function assumes that UseName has been error checked and
|
||
canonicalized.
|
||
|
||
Arguments:
|
||
|
||
UseName - Supplies the local device name or shared resource name of
|
||
the tree connection.
|
||
|
||
Level - Supplies the level of information to be returned regarding the
|
||
specified tree connection.
|
||
|
||
BufferPointer - Returns a pointer to the buffer allocated by the
|
||
Workstation service which contains the requested information.
|
||
This pointer is set to NULL if return code is not NERR_Success
|
||
or ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
NET_API_STATUS status;
|
||
|
||
LUID LogonId; // Logon Id of user
|
||
DWORD Index; // Index to user entry in Use Table
|
||
|
||
PUSE_ENTRY MatchedPointer; // Points to found use entry
|
||
HANDLE TreeConnection; // Handle to connection
|
||
|
||
TCHAR *FormattedUseName;
|
||
// For canonicalizing a local device
|
||
// name
|
||
DWORD PathType = 0;
|
||
|
||
LPBYTE Buffer = NULL;
|
||
|
||
PUSE_ENTRY UseList;
|
||
|
||
SET_USE_INFO_POINTER(InfoStruct, NULL);
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
if (Level > 3) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
FormattedUseName = (TCHAR *)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,(MAX_PATH+1)*sizeof(TCHAR));
|
||
|
||
if (FormattedUseName == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Check to see if UseName is valid, and canonicalize it.
|
||
//
|
||
if (I_NetPathCanonicalize(
|
||
NULL,
|
||
UseName,
|
||
FormattedUseName,
|
||
(MAX_PATH+1)*sizeof(TCHAR),
|
||
NULL,
|
||
&PathType,
|
||
0
|
||
) != NERR_Success) {
|
||
LocalFree(FormattedUseName);
|
||
return NERR_UseNotFound;
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetUseGetInfo %ws %lu\n", FormattedUseName, Level));
|
||
}
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
|
||
LocalFree(FormattedUseName);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Lock Use Table for read access
|
||
//
|
||
if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
|
||
LocalFree(FormattedUseName);
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
//
|
||
// See if the use entry is an explicit connection.
|
||
//
|
||
status = WsGetUserEntry(
|
||
&Use,
|
||
&LogonId,
|
||
&Index,
|
||
FALSE
|
||
);
|
||
|
||
UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
|
||
NULL;
|
||
|
||
if ((status = WsFindUse(
|
||
&LogonId,
|
||
UseList,
|
||
FormattedUseName,
|
||
&TreeConnection,
|
||
&MatchedPointer,
|
||
NULL
|
||
)) != NERR_Success) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
LocalFree(FormattedUseName);
|
||
return status;
|
||
}
|
||
|
||
LocalFree(FormattedUseName);
|
||
|
||
if (MatchedPointer == NULL) {
|
||
|
||
//
|
||
// UseName specified has an implicit connection. Don't need to hold
|
||
// on to Use Table anymore.
|
||
//
|
||
RtlReleaseResource(&Use.TableResource);
|
||
}
|
||
|
||
status = WsGetUseInfo(
|
||
&LogonId,
|
||
Level,
|
||
TreeConnection,
|
||
MatchedPointer,
|
||
&Buffer
|
||
);
|
||
|
||
if (MatchedPointer == NULL) {
|
||
//
|
||
// Close temporary handle to implicit connection.
|
||
//
|
||
NtClose(TreeConnection);
|
||
}
|
||
else {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
}
|
||
|
||
SET_USE_INFO_POINTER(InfoStruct, Buffer);
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseGetInfo: about to return status=%lu\n",
|
||
status));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrUseEnum(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN OUT LPUSE_ENUM_STRUCT InfoStruct,
|
||
IN DWORD PreferedMaximumLength,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetUseEnum entry point in the Workstation service.
|
||
|
||
Arguments:
|
||
|
||
ServerName - Supplies the name of server to execute this function
|
||
|
||
InfoStruct - 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;
|
||
|
||
LUID LogonId; // Logon Id of user
|
||
DWORD Index; // Index to user entry in Use Table
|
||
PUSE_ENTRY UseList; // Pointer to user's use list
|
||
|
||
DWORD EnumConnectionHint = 0; // Hint size from redirector
|
||
LMR_REQUEST_PACKET Rrp; // Redirector request packet
|
||
|
||
DWORD TotalImplicit; // Length of ImplicitList
|
||
LPBYTE ImplicitList; // List of information on implicit
|
||
// connections
|
||
LPBYTE Buffer = NULL;
|
||
DWORD EntriesRead = 0;
|
||
DWORD Level = InfoStruct->Level;
|
||
|
||
if (Level > 2) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
if (InfoStruct->UseInfo.Level2 == NULL) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
try {
|
||
SET_USE_ENUM_POINTER(InfoStruct, NULL, 0);
|
||
}
|
||
except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Ask the redirector to enumerate the information of implicit connections
|
||
// established by the caller.
|
||
//
|
||
Rrp.Type = GetConnectionInfo;
|
||
Rrp.Version = REQUEST_PACKET_VERSION;
|
||
RtlCopyLuid(&Rrp.LogonId, &LogonId);
|
||
Rrp.Level = Level;
|
||
Rrp.Parameters.Get.ResumeHandle = 0;
|
||
|
||
if ((status = WsDeviceControlGetInfo(
|
||
Redirector,
|
||
WsRedirDeviceHandle,
|
||
FSCTL_LMR_ENUMERATE_CONNECTIONS,
|
||
&Rrp,
|
||
sizeof(LMR_REQUEST_PACKET),
|
||
(LPBYTE *) &ImplicitList,
|
||
MAXULONG,
|
||
EnumConnectionHint,
|
||
NULL
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If successful in getting all the implicit connection info from the
|
||
// redirector, expect the total entries available to be equal to entries
|
||
// read.
|
||
//
|
||
TotalImplicit = Rrp.Parameters.Get.TotalEntries;
|
||
NetpAssert(TotalImplicit == Rrp.Parameters.Get.EntriesRead);
|
||
|
||
//
|
||
// Serialize access to Use Table.
|
||
//
|
||
if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
|
||
status = NERR_InternalError;
|
||
goto CleanUp;
|
||
}
|
||
|
||
//
|
||
// See if the user has explicit connection entries in the Use Table.
|
||
//
|
||
status = WsGetUserEntry(
|
||
&Use,
|
||
&LogonId,
|
||
&Index,
|
||
FALSE
|
||
);
|
||
|
||
UseList = (status == NERR_Success) ? (PUSE_ENTRY) Use.Table[Index].List :
|
||
NULL;
|
||
|
||
//
|
||
// User has no connections if both implicit and explicit lists are empty.
|
||
//
|
||
if (TotalImplicit == 0 && UseList == NULL) {
|
||
*TotalEntries = 0;
|
||
status = NERR_Success;
|
||
goto CleanUp;
|
||
}
|
||
|
||
status = WsEnumUseInfo(
|
||
&LogonId,
|
||
Level,
|
||
UseList,
|
||
ImplicitList,
|
||
TotalImplicit,
|
||
&Buffer,
|
||
PreferedMaximumLength,
|
||
&EntriesRead,
|
||
TotalEntries,
|
||
ResumeHandle
|
||
);
|
||
|
||
CleanUp:
|
||
MIDL_user_free(ImplicitList);
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
|
||
SET_USE_ENUM_POINTER(InfoStruct, Buffer, EntriesRead);
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseEnum: about to return status=%lu\n",
|
||
status));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN HANDLE TreeConnection,
|
||
IN PUSE_ENTRY UseEntry,
|
||
OUT LPBYTE *OutputBuffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates the output buffer of exactly the required size
|
||
and fill it with the use information that is requested by the caller of
|
||
NetUseGetInfo.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
TreeConnection - Supplies the handle to the tree connection which user is
|
||
requesting information about.
|
||
|
||
UseEntry - Supplies a pointer to the use entry if the tree connection is
|
||
an explicit connection.
|
||
|
||
OutputBuffer - Returns a pointer to the buffer allocated by this
|
||
routine which contains the use information requested. This pointer
|
||
is set to NULL if return code is not NERR_Success.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
|
||
DWORD OutputBufferLength;
|
||
|
||
LPBYTE FixedPortion;
|
||
LPTSTR EndOfVariableData;
|
||
|
||
PLMR_CONNECTION_INFO_2 ConnectionInfo;
|
||
|
||
|
||
//
|
||
// Get information of the requested connection from redirector
|
||
// Only send Level 0,1,2 to redir. Send 2 in place of 3.
|
||
//
|
||
if ((status = WsGetRedirUseInfo(
|
||
LogonId,
|
||
(Level > 2 ? 2 : Level),
|
||
TreeConnection,
|
||
(LPBYTE *) &ConnectionInfo
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
OutputBufferLength =
|
||
USE_TOTAL_LENGTH(
|
||
Level,
|
||
((UseEntry != NULL) ?
|
||
(UseEntry->LocalLength + UseEntry->Remote->UncNameLength +
|
||
2) * sizeof(TCHAR) :
|
||
ConnectionInfo->UNCName.Length + (2 * sizeof(TCHAR))),
|
||
ConnectionInfo->UserName.Length + sizeof(TCHAR)
|
||
);
|
||
|
||
if( Level >= 2 && ConnectionInfo->DomainName.Length != 0 ) {
|
||
OutputBufferLength += ConnectionInfo->DomainName.Length + sizeof(TCHAR);
|
||
}
|
||
|
||
//
|
||
// Allocate output buffer to be filled in and returned to user
|
||
//
|
||
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
|
||
|
||
FixedPortion = *OutputBuffer;
|
||
EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion + OutputBufferLength);
|
||
|
||
if (UseEntry != NULL) {
|
||
//
|
||
// Use the UNC name of WorkStation Services instead of RDR which doesn't include the
|
||
// deep net use path
|
||
//
|
||
|
||
ConnectionInfo->UNCName.Length =
|
||
ConnectionInfo->UNCName.MaximumLength = (USHORT)UseEntry->Remote->UncNameLength * sizeof(TCHAR);
|
||
ConnectionInfo->UNCName.Buffer = (PWSTR)UseEntry->Remote->UncName;
|
||
}
|
||
|
||
//
|
||
// Combine the redirector information (if any) with the use entry
|
||
// information into one output buffer.
|
||
//
|
||
status = WsGetCombinedUseInfo(
|
||
Level,
|
||
USE_FIXED_LENGTH(Level),
|
||
UseEntry,
|
||
ConnectionInfo,
|
||
&FixedPortion,
|
||
&EndOfVariableData,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// We should have allocated enough memory for all the data
|
||
//
|
||
NetpAssert(status == NERR_Success);
|
||
|
||
//
|
||
// If not successful in getting any data, free the output buffer and set
|
||
// it to NULL.
|
||
//
|
||
if (status != NERR_Success) {
|
||
MIDL_user_free(*OutputBuffer);
|
||
*OutputBuffer = NULL;
|
||
}
|
||
|
||
MIDL_user_free(ConnectionInfo);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN PUSE_ENTRY UseList,
|
||
IN LPBYTE ImplicitList,
|
||
IN DWORD TotalImplicit,
|
||
OUT LPBYTE *OutputBuffer,
|
||
IN DWORD PreferedMaximumLength,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD TotalEntries,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates the output buffer of exactly the required size
|
||
and fill it with the use information that is requested by the caller of
|
||
NetUseEnum.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
UseList - Supplies a pointer to the use list.
|
||
|
||
ImplicitList - Supplies an array of information from the redirector
|
||
about each implicit connection.
|
||
|
||
TotalImplicit - Supplies the number of entries in ImplicitList.
|
||
|
||
OutputBuffer - Returns a pointer to the buffer allocated by this
|
||
routine which contains the use information requested. This pointer
|
||
is set to NULL if return code is not NERR_Success.
|
||
|
||
PreferedMaximumLength - Supplies the number of bytes of information
|
||
to return in the buffer. If this value is MAXULONG, we will try
|
||
to return all available information if there is enough memory
|
||
resource.
|
||
|
||
EntriesRead - Returns the number of entries read into the buffer. This
|
||
value is returned only if the return code is NERR_Success or
|
||
ERROR_MORE_DATA.
|
||
|
||
TotalEntries - Returns the remaining total number of entries that would
|
||
be read into output buffer if it has enough memory to hold all entries.
|
||
This value is returned only if the return code is NERR_Success or
|
||
ERROR_MORE_DATA.
|
||
|
||
ResumeHandle - Supplies the resume key to begin enumeration, and returns
|
||
the key to the next entry to resume the enumeration if the current
|
||
call returns ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
DWORD i;
|
||
DWORD OutputBufferLength = 0;
|
||
PUSE_ENTRY UseEntry = UseList;
|
||
DWORD TotalExplicit = 0;
|
||
|
||
//
|
||
// Get the use information from the redirector for each explicit connection
|
||
//
|
||
while (UseEntry != NULL) {
|
||
|
||
PLMR_CONNECTION_INFO_2 ci2;
|
||
|
||
//
|
||
// Get tree connection information from the redirector.
|
||
//
|
||
|
||
ci2 = NULL;
|
||
|
||
if ((status = WsGetRedirUseInfo(
|
||
LogonId,
|
||
Level,
|
||
UseEntry->TreeConnection,
|
||
(LPBYTE *) &ci2
|
||
)) != NERR_Success) {
|
||
|
||
if( ci2 != NULL )
|
||
MIDL_user_free( ci2 );
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
if( ci2 == NULL ) {
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
//
|
||
// Use the UNC name of WorkStation Services instead of RDR which doesn't include the
|
||
// deep net use path
|
||
//
|
||
|
||
ci2->UNCName.Length =
|
||
ci2->UNCName.MaximumLength = (USHORT)UseEntry->Remote->UncNameLength * sizeof(TCHAR);
|
||
ci2->UNCName.Buffer = (PWSTR)UseEntry->Remote->UncName;
|
||
|
||
//
|
||
// While we are here, add up the amount of memory needed to hold the
|
||
// explicit connection entries including information from the redir
|
||
// like username.
|
||
//
|
||
if (PreferedMaximumLength == MAXULONG) {
|
||
OutputBufferLength +=
|
||
USE_TOTAL_LENGTH(
|
||
Level,
|
||
(UseEntry->LocalLength +
|
||
ci2->UNCName.Length +
|
||
2) * sizeof(TCHAR),
|
||
(ci2->UserName.Length +
|
||
sizeof(TCHAR))
|
||
);
|
||
|
||
if( Level >= 2 && ci2->DomainName.Length != 0 ) {
|
||
OutputBufferLength += ci2->DomainName.Length + sizeof(TCHAR);
|
||
}
|
||
}
|
||
|
||
MIDL_user_free( ci2 );
|
||
|
||
//
|
||
// Sum up the number of explicit connections.
|
||
//
|
||
TotalExplicit++;
|
||
|
||
UseEntry = UseEntry->Next;
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseEnum: length of explicit info %lu\n",
|
||
OutputBufferLength));
|
||
}
|
||
|
||
//
|
||
// If the user requests to enumerate all use entries with
|
||
// PreferedMaximumLength == MAXULONG, add up the total number of bytes
|
||
// we need to allocate for the output buffer. We know the amount we
|
||
// need for explicit connections from above; now add the lengths of
|
||
// implicit connection information.
|
||
//
|
||
|
||
if (PreferedMaximumLength == MAXULONG) {
|
||
|
||
//
|
||
// Pointer to the next entry in the ImplicitList is computed based
|
||
// on the level of information requested from the redirector.
|
||
//
|
||
LPBYTE ImplicitEntry;
|
||
DWORD ImplicitEntryLength = REDIR_ENUM_INFO_FIXED_LENGTH(Level);
|
||
|
||
|
||
//
|
||
// Add up the buffer size needed to hold the implicit connection
|
||
// information
|
||
//
|
||
for (ImplicitEntry = ImplicitList, i = 0; i < TotalImplicit;
|
||
ImplicitEntry += ImplicitEntryLength, i++) {
|
||
|
||
OutputBufferLength +=
|
||
USE_TOTAL_LENGTH(
|
||
Level,
|
||
((PLMR_CONNECTION_INFO_2) ImplicitEntry)->UNCName.Length
|
||
+ (2 * sizeof(TCHAR)),
|
||
((PLMR_CONNECTION_INFO_2) ImplicitEntry)->UserName.Length
|
||
+ sizeof(TCHAR)
|
||
);
|
||
|
||
if( Level >= 2 ) {
|
||
OutputBufferLength += (DNS_MAX_NAME_LENGTH + 1)*sizeof(TCHAR);
|
||
}
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint((
|
||
"[Wksta] NetrUseEnum: length of implicit & explicit info %lu\n",
|
||
OutputBufferLength));
|
||
}
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// We will return as much as possible that fits into this specified
|
||
// buffer size.
|
||
//
|
||
OutputBufferLength = ROUND_UP_COUNT(PreferedMaximumLength, ALIGN_WCHAR);
|
||
|
||
if (OutputBufferLength < USE_FIXED_LENGTH(Level)) {
|
||
|
||
*OutputBuffer = NULL;
|
||
*EntriesRead = 0;
|
||
*TotalEntries = TotalExplicit + TotalImplicit;
|
||
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Allocate the output buffer
|
||
//
|
||
if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
|
||
|
||
|
||
//
|
||
// Get the information
|
||
//
|
||
status = WsEnumCombinedUseInfo(
|
||
LogonId,
|
||
Level,
|
||
ImplicitList,
|
||
TotalImplicit,
|
||
UseList,
|
||
*OutputBuffer,
|
||
OutputBufferLength,
|
||
EntriesRead,
|
||
TotalEntries,
|
||
ResumeHandle
|
||
);
|
||
|
||
//
|
||
// WsEnumCombinedUseInfo returns in *TotalEntries the number of
|
||
// remaining unread entries. Therefore, the real total is the
|
||
// sum of this returned value and the number of entries read.
|
||
//
|
||
(*TotalEntries) += (*EntriesRead);
|
||
|
||
//
|
||
// If the caller asked for all available data with
|
||
// PreferedMaximumLength == MAXULONG and our buffer overflowed, free the
|
||
// output buffer and set its pointer to NULL.
|
||
//
|
||
if (PreferedMaximumLength == MAXULONG && status == ERROR_MORE_DATA) {
|
||
|
||
MIDL_user_free(*OutputBuffer);
|
||
*OutputBuffer = NULL;
|
||
|
||
//
|
||
// PreferedMaximumLength == MAXULONG and buffer overflowed means
|
||
// we do not have enough memory to satisfy the request.
|
||
//
|
||
if (status == ERROR_MORE_DATA) {
|
||
status = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
else {
|
||
|
||
if (*EntriesRead == 0) {
|
||
MIDL_user_free(*OutputBuffer);
|
||
*OutputBuffer = NULL;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsEnumCombinedUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN LPBYTE ImplicitList,
|
||
IN DWORD TotalImplicit,
|
||
IN PUSE_ENTRY UseList,
|
||
OUT LPBYTE OutputBuffer,
|
||
IN DWORD OutputBufferLength,
|
||
OUT LPDWORD EntriesRead,
|
||
OUT LPDWORD EntriesUnread,
|
||
IN OUT LPDWORD ResumeHandle OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function lists all existing connections by going through the
|
||
the explicit connections in the Use Table, and the implicit connections
|
||
from the redirector.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
ImplicitList - Supplies an array implicit connections from the redirector.
|
||
|
||
TotalImplicit - Supplies the number of entries in ImplicitList.
|
||
|
||
UseList - Supplies a pointer to the use list.
|
||
|
||
OutputBuffer - Supplies the output buffer which receives the requested
|
||
information.
|
||
|
||
OutputBufferLength - Supplies the length of the output buffer.
|
||
|
||
EntriesRead - Returns the number of entries written into the output
|
||
buffer.
|
||
|
||
EntriesUnread - Returns the remaining total number of unread entries.
|
||
This value is returned only if the return code is NERR_Success or
|
||
ERROR_MORE_DATA.
|
||
|
||
ResumeHandle - Supplies the resume key to begin enumeration, and returns
|
||
the key to the next entry to resume the enumeration if the current
|
||
call returns ERROR_MORE_DATA.
|
||
|
||
Return Value:
|
||
|
||
NERR_Success - All entries fit into the output buffer.
|
||
|
||
ERROR_MORE_DATA - 0 or more entries were written into the output buffer
|
||
but not all entries fit.
|
||
|
||
--*/
|
||
{
|
||
DWORD i;
|
||
NET_API_STATUS status;
|
||
DWORD UseFixedLength = USE_FIXED_LENGTH(Level);
|
||
|
||
LPBYTE FixedPortion = OutputBuffer;
|
||
LPTSTR EndOfVariableData = (LPTSTR) ((DWORD_PTR) FixedPortion +
|
||
OutputBufferLength);
|
||
//
|
||
// Pointer to the next entry in the ImplicitList is computed based on the
|
||
// level of information requested from the redirector.
|
||
//
|
||
LPBYTE ImplicitEntry;
|
||
DWORD ImplicitEntryLength = REDIR_ENUM_INFO_FIXED_LENGTH(Level);
|
||
|
||
DWORD StartEnumeration = 0;
|
||
BOOL OnlyRedirectorList = FALSE;
|
||
|
||
|
||
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
||
StartEnumeration = *ResumeHandle & ~(REDIR_LIST);
|
||
OnlyRedirectorList = *ResumeHandle & REDIR_LIST;
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("\nStartEnumeration=%lu\n, OnlyRedir=%u\n",
|
||
StartEnumeration, OnlyRedirectorList));
|
||
}
|
||
|
||
*EntriesRead = 0;
|
||
|
||
//
|
||
// Enumerate explicit connections. This is done only if resume handle
|
||
// says to start enumeration from the explicit list.
|
||
//
|
||
if (! OnlyRedirectorList) {
|
||
|
||
for( ; UseList != NULL; UseList = UseList->Next ) {
|
||
|
||
PLMR_CONNECTION_INFO_2 ci2;
|
||
|
||
if( StartEnumeration > UseList->ResumeKey ) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get tree connection information from the redirector.
|
||
//
|
||
|
||
ci2 = NULL;
|
||
|
||
status = WsGetRedirUseInfo( LogonId, Level, UseList->TreeConnection, (LPBYTE *) &ci2 );
|
||
|
||
if( status != NERR_Success || ci2 == NULL ) {
|
||
if( ci2 != NULL )
|
||
MIDL_user_free( ci2 );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Use the UNC name of WorkStation Services instead of RDR which doesn't include the
|
||
// deep net use path
|
||
//
|
||
|
||
ci2->UNCName.Length =
|
||
ci2->UNCName.MaximumLength = (USHORT)UseList->Remote->UncNameLength * sizeof(TCHAR);
|
||
ci2->UNCName.Buffer = (PWSTR)UseList->Remote->UncName;
|
||
|
||
status = WsGetCombinedUseInfo(
|
||
Level,
|
||
UseFixedLength,
|
||
UseList,
|
||
ci2,
|
||
&FixedPortion,
|
||
&EndOfVariableData,
|
||
EntriesRead );
|
||
|
||
MIDL_user_free( ci2 );
|
||
|
||
if( status == ERROR_MORE_DATA ) {
|
||
|
||
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
||
*ResumeHandle = UseList->ResumeKey;
|
||
}
|
||
|
||
*EntriesUnread = TotalImplicit;
|
||
|
||
while (UseList != NULL) {
|
||
(*EntriesUnread)++;
|
||
UseList = UseList->Next;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finished the explicit list. Start from the beginning of implicit
|
||
// list.
|
||
//
|
||
StartEnumeration = 0;
|
||
}
|
||
|
||
//
|
||
// Enumerate implicit connections
|
||
//
|
||
for (ImplicitEntry = ImplicitList, i = 0; i < TotalImplicit;
|
||
ImplicitEntry += ImplicitEntryLength, i++) {
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("RedirList->ResumeKey=%lu\n",
|
||
((PLMR_CONNECTION_INFO_2) ImplicitEntry)->ResumeKey));
|
||
}
|
||
|
||
if (StartEnumeration <=
|
||
((PLMR_CONNECTION_INFO_2) ImplicitEntry)->ResumeKey) {
|
||
|
||
if (WsGetCombinedUseInfo(
|
||
Level,
|
||
UseFixedLength,
|
||
NULL,
|
||
(PLMR_CONNECTION_INFO_2) ImplicitEntry,
|
||
&FixedPortion,
|
||
&EndOfVariableData,
|
||
EntriesRead
|
||
) == ERROR_MORE_DATA) {
|
||
|
||
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
||
*ResumeHandle = ((PLMR_CONNECTION_INFO_2)
|
||
ImplicitEntry)->ResumeKey;
|
||
*ResumeHandle |= REDIR_LIST;
|
||
}
|
||
|
||
*EntriesUnread = TotalImplicit - i;
|
||
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Successful enumeration. Reset the resume handle to start from the
|
||
// beginning.
|
||
//
|
||
if (ARGUMENT_PRESENT(ResumeHandle)) {
|
||
*ResumeHandle = 0;
|
||
}
|
||
|
||
//
|
||
// There are no more remaining entries.
|
||
//
|
||
*EntriesUnread = 0;
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetRedirUseInfo(
|
||
IN PLUID LogonId,
|
||
IN DWORD Level,
|
||
IN HANDLE TreeConnection,
|
||
OUT LPBYTE *OutputBuffer
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets the connection information from the redirector given
|
||
the handle to the connection.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
TreeConnection - Supplies the handle to the tree connection which user is
|
||
requesting information about.
|
||
|
||
OutputBuffer - Returns a pointer to the buffer allocated by this
|
||
routine which contains the connection information requested. This
|
||
pointer is set to NULL if return code is not NERR_Success.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
LMR_REQUEST_PACKET Rrp;
|
||
|
||
//
|
||
// Get information of the requested connection from redirector
|
||
//
|
||
Rrp.Type = GetConnectionInfo;
|
||
Rrp.Version = REQUEST_PACKET_VERSION;
|
||
RtlCopyLuid(&Rrp.LogonId, LogonId);
|
||
Rrp.Level = Level;
|
||
Rrp.Parameters.Get.ResumeHandle = 0;
|
||
Rrp.Parameters.Get.TotalBytesNeeded = 0;
|
||
|
||
return WsDeviceControlGetInfo(
|
||
Redirector,
|
||
TreeConnection,
|
||
FSCTL_LMR_GET_CONNECTION_INFO,
|
||
&Rrp,
|
||
sizeof(LMR_REQUEST_PACKET),
|
||
OutputBuffer,
|
||
MAXULONG,
|
||
HINT_REDIR_INFO(Level),
|
||
NULL
|
||
);
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsGetCombinedUseInfo(
|
||
IN DWORD Level,
|
||
IN DWORD UseFixedLength,
|
||
IN PUSE_ENTRY UseEntry,
|
||
IN PLMR_CONNECTION_INFO_2 UncEntry,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN OUT LPDWORD EntriesRead OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function puts together the use information from redirector and from
|
||
the Use Table (if any) into the output buffer. It increments the
|
||
EntriesRead variable when a use entry is written into the output buffer.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information to be returned.
|
||
|
||
UseFixedLength - Supplies the length of the fixed portion of the use
|
||
information returned.
|
||
|
||
UseEntry - Supplies the pointer to the use entry in the Use Table if it
|
||
is an explicit connection.
|
||
|
||
UncEntry - Supplies a pointer to the use information retrieved from the
|
||
redirector.
|
||
|
||
FixedPortion - Supplies a pointer to the output buffer where the next
|
||
entry of the fixed portion of the use information will be written.
|
||
This pointer is updated to point to the next fixed portion entry
|
||
after a use entry is written.
|
||
|
||
EndOfVariableData - Supplies a pointer just off the last available byte
|
||
in the output buffer. This is because the variable portion of the use
|
||
information is written into the output buffer starting from the end.
|
||
This pointer is updated after any variable length information is
|
||
written to the output buffer.
|
||
|
||
EntriesRead - Supplies a running total of the number of entries read
|
||
into the buffer. This value is incremented every time a use entry is
|
||
successfully written into the output buffer.
|
||
|
||
Return Value:
|
||
|
||
NERR_Success - The current entry fits into the output buffer.
|
||
|
||
ERROR_MORE_DATA - The current entry does not fit into the output buffer.
|
||
|
||
--*/
|
||
{
|
||
if (((DWORD_PTR) *FixedPortion + UseFixedLength) >=
|
||
(DWORD_PTR) *EndOfVariableData) {
|
||
|
||
//
|
||
// Fixed length portion does not fit.
|
||
//
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
if (! WsFillUseBuffer(
|
||
Level,
|
||
UseEntry,
|
||
UncEntry,
|
||
FixedPortion,
|
||
EndOfVariableData,
|
||
UseFixedLength
|
||
)) {
|
||
|
||
//
|
||
// Variable length portion does not fit.
|
||
//
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(EntriesRead)) {
|
||
(*EntriesRead)++;
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
BOOL
|
||
WsFillUseBuffer(
|
||
IN DWORD Level,
|
||
IN PUSE_ENTRY UseEntry,
|
||
IN PLMR_CONNECTION_INFO_2 UncEntry,
|
||
IN OUT LPBYTE *FixedPortion,
|
||
IN OUT LPTSTR *EndOfVariableData,
|
||
IN DWORD UseFixedLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function fills an entry in the output buffer with the supplied use
|
||
information, and updates the FixedPortion and EndOfVariableData pointers.
|
||
|
||
NOTE: This function assumes that the fixed size portion will fit into
|
||
the output buffer.
|
||
|
||
It also assumes that info structure level 2 is a superset of
|
||
info structure level 1, which in turn is a superset of info
|
||
structure level 0, 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.
|
||
|
||
UseEntry - Supplies the pointer to the use entry in the Use Table if it is
|
||
an explicit connection.
|
||
|
||
UncEntry - Supplies a pointer to the use information retrieved from the
|
||
redirector.
|
||
|
||
FixedPortion - Supplies a pointer to the output buffer where the next
|
||
entry of the fixed portion of the use information will be written.
|
||
This pointer is updated after a use entry is written to the
|
||
output buffer.
|
||
|
||
EndOfVariableData - Supplies a pointer just off the last available byte
|
||
in the output buffer. This is because the variable portion of the use
|
||
information is written into the output buffer starting from the end.
|
||
This pointer is updated after any variable length information is
|
||
written to the output buffer.
|
||
|
||
UseFixedLength - Supplies the number of bytes needed to hold the fixed
|
||
size portion.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if entire entry fits into output buffer, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
PUSE_INFO_2 UseInfo = (PUSE_INFO_2) *FixedPortion;
|
||
|
||
|
||
*FixedPortion += UseFixedLength;
|
||
|
||
switch (Level) {
|
||
case 3:
|
||
if(UseEntry != NULL && (UseEntry->Flags & USE_DEFAULT_CREDENTIALS)) {
|
||
((PUSE_INFO_3)*FixedPortion)->ui3_flags |= USE_DEFAULT_CREDENTIALS;
|
||
}
|
||
|
||
case 2:
|
||
if (! NetpCopyStringToBuffer(
|
||
UncEntry->UserName.Buffer,
|
||
UncEntry->UserName.Length / sizeof(TCHAR),
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
&UseInfo->ui2_username
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if( UncEntry->DomainName.Length != 0 ) {
|
||
if(! NetpCopyStringToBuffer(
|
||
UncEntry->DomainName.Buffer,
|
||
UncEntry->DomainName.Length / sizeof(TCHAR),
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
&UseInfo->ui2_domainname
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
case 1:
|
||
|
||
UseInfo->ui2_password = NULL;
|
||
|
||
UseInfo->ui2_status = UncEntry->ConnectionStatus;
|
||
|
||
if ((UseEntry != NULL) && (UseEntry->Local != NULL)
|
||
&& (UseEntry->LocalLength > 2)) {
|
||
|
||
//
|
||
// Reassign the status of the connection if it is paused
|
||
//
|
||
if (WsRedirectionPaused(UseEntry->Local)) {
|
||
UseInfo->ui2_status = USE_PAUSED;
|
||
}
|
||
}
|
||
|
||
switch (UncEntry->SharedResourceType) {
|
||
|
||
case FILE_DEVICE_DISK:
|
||
UseInfo->ui2_asg_type = USE_DISKDEV;
|
||
break;
|
||
|
||
case FILE_DEVICE_PRINTER:
|
||
UseInfo->ui2_asg_type = USE_SPOOLDEV;
|
||
break;
|
||
|
||
case FILE_DEVICE_SERIAL_PORT:
|
||
UseInfo->ui2_asg_type = USE_CHARDEV;
|
||
break;
|
||
|
||
case FILE_DEVICE_NAMED_PIPE:
|
||
UseInfo->ui2_asg_type = USE_IPC;
|
||
break;
|
||
|
||
default:
|
||
NetpKdPrint((
|
||
"WsFillUseBuffer: Unknown shared resource type %d.\n",
|
||
UncEntry->SharedResourceType
|
||
));
|
||
|
||
case FILE_DEVICE_UNKNOWN:
|
||
UseInfo->ui2_asg_type = USE_WILDCARD;
|
||
break;
|
||
}
|
||
|
||
UseInfo->ui2_refcount = UncEntry->NumberFilesOpen;
|
||
|
||
UseInfo->ui2_usecount = (UseEntry == NULL) ? 0 :
|
||
UseEntry->Remote->TotalUseCount;
|
||
|
||
case 0:
|
||
|
||
if (UseEntry != NULL) {
|
||
//
|
||
// Explicit connection
|
||
//
|
||
if (! NetpCopyStringToBuffer(
|
||
UseEntry->Local,
|
||
UseEntry->LocalLength,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
&UseInfo->ui2_local
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
else {
|
||
//
|
||
// Implicit connection
|
||
//
|
||
if (! NetpCopyStringToBuffer(
|
||
NULL,
|
||
0,
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
&UseInfo->ui2_local
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (! NetpCopyStringToBuffer(
|
||
UncEntry->UNCName.Buffer,
|
||
UncEntry->UNCName.Length / sizeof(TCHAR),
|
||
*FixedPortion,
|
||
EndOfVariableData,
|
||
&UseInfo->ui2_remote
|
||
)) {
|
||
return FALSE;
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// This should never happen.
|
||
//
|
||
NetpKdPrint(("WsFillUseBuffer: Invalid level %u.\n", Level));
|
||
NetpAssert(FALSE);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|