windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/usegenum.c
2020-09-26 16:20:57 +08:00

1423 lines
40 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}