1568 lines
41 KiB
C
1568 lines
41 KiB
C
/*++
|
||
|
||
Copyright (c) 1991-92 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
useaddel.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the worker routines for the NetUseAdd and
|
||
NetUseDel APIs implemented in the Workstation service.
|
||
|
||
Author:
|
||
|
||
Rita Wong (ritaw) 4-Mar-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "wsutil.h"
|
||
#include "wsdevice.h"
|
||
#include "wsuse.h"
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Local function prototypes //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsAddUse(
|
||
IN PLUID LogonId,
|
||
IN HANDLE TreeConnection,
|
||
IN LPTSTR Local OPTIONAL,
|
||
IN DWORD LocalLength,
|
||
IN LPTSTR UncName,
|
||
IN DWORD UncNameLength,
|
||
IN PUNICODE_STRING TreeConnectStr,
|
||
IN DWORD Flags
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsDeleteUse(
|
||
IN PLUID LogonId,
|
||
IN DWORD ForceLevel,
|
||
IN PUSE_ENTRY MatchedPointer,
|
||
IN DWORD Index
|
||
);
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCreateNewEntry(
|
||
OUT PUSE_ENTRY *NewUse,
|
||
IN HANDLE TreeConnection,
|
||
IN LPTSTR Local OPTIONAL,
|
||
IN DWORD LocalLength,
|
||
IN LPTSTR UncName OPTIONAL,
|
||
IN DWORD UncNameLength,
|
||
IN PUNICODE_STRING TreeConnectStr,
|
||
IN DWORD Flags
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCheckLocalAndDeviceType(
|
||
IN LPTSTR Local,
|
||
IN DWORD DeviceType,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCheckEstablishedDeviceType(
|
||
IN HANDLE TreeConnection,
|
||
IN DWORD RequestedDeviceType
|
||
);
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsAllocateUseWorkBuffer(
|
||
IN PUSE_INFO_2 UseInfo,
|
||
IN DWORD Level,
|
||
OUT LPTSTR *UncName,
|
||
OUT LPTSTR *Local,
|
||
OUT LPTSTR *UserName,
|
||
OUT LPTSTR *DomainName
|
||
);
|
||
|
||
#if DBG
|
||
|
||
STATIC
|
||
VOID
|
||
DumpUseList(
|
||
DWORD Index
|
||
);
|
||
|
||
#endif
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Global variables //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
//
|
||
// Monotonically incrementing integer. A unique value is assigned to
|
||
// each new use entry created so that we can provide an enumeration
|
||
// resume handle.
|
||
//
|
||
STATIC DWORD GlobalResumeKey = 0;
|
||
|
||
//-------------------------------------------------------------------//
|
||
// //
|
||
// Macros //
|
||
// //
|
||
//-------------------------------------------------------------------//
|
||
|
||
#define GET_USE_INFO_POINTER(UseInfo, InfoStruct) \
|
||
UseInfo = InfoStruct->UseInfo3;
|
||
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrUseAdd(
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN DWORD Level,
|
||
IN LPUSE_INFO InfoStruct,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetUseAdd entry point in the Workstation service.
|
||
|
||
Arguments:
|
||
|
||
Level - Supplies the level of information specified in Buffer.
|
||
|
||
Buffer - Supplies the parameters to create the new tree connection with.
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
LUID LogonId;
|
||
NET_API_STATUS status;
|
||
|
||
LPTSTR UncName = NULL;
|
||
DWORD UncNameLength = 0;
|
||
PTSTR Local = NULL;
|
||
DWORD LocalLength = 0;
|
||
DWORD Flags;
|
||
|
||
LPTSTR UserName = NULL;
|
||
LPTSTR DomainName = NULL;
|
||
LPTSTR Password = NULL;
|
||
UNICODE_STRING EncodedPassword;
|
||
ULONG CreateFlags;
|
||
|
||
HANDLE TreeConnection;
|
||
UNICODE_STRING TreeConnectStr;
|
||
|
||
PUSE_INFO_3 pUseInfo;
|
||
PUSE_INFO_2 UseInfo;
|
||
|
||
DWORD SessionId;
|
||
LPWSTR Session = NULL;
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
if (Level == 0) {
|
||
return ERROR_INVALID_LEVEL;
|
||
}
|
||
|
||
#define NETR_USE_ADD_PASSWORD_SEED 0x56 // Pick a non-zero seed
|
||
RtlInitUnicodeString( &EncodedPassword, NULL );
|
||
|
||
GET_USE_INFO_POINTER(pUseInfo, InfoStruct);
|
||
|
||
if (pUseInfo == NULL) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, PARM_ERROR_UNKNOWN);
|
||
}
|
||
|
||
//
|
||
// Cast a pointer to USE_INFO_2 to make things easy ...
|
||
//
|
||
|
||
UseInfo = &pUseInfo->ui3_ui2;
|
||
|
||
if(Level == 3)
|
||
{
|
||
CreateFlags = pUseInfo->ui3_flags;
|
||
}
|
||
else
|
||
{
|
||
CreateFlags = 0;
|
||
}
|
||
|
||
//
|
||
// UNC name can never be NULL or empty string.
|
||
//
|
||
if ((UseInfo->ui2_remote == NULL) ||
|
||
(UseInfo->ui2_remote[0] == TCHAR_EOS)) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
|
||
}
|
||
|
||
//
|
||
// Allocate one large buffer for storing the UNC name, local device name,
|
||
// username, and domain name.
|
||
//
|
||
if ((status = WsAllocateUseWorkBuffer(
|
||
UseInfo,
|
||
Level,
|
||
&UncName, // Free using this pointer
|
||
&Local,
|
||
&UserName,
|
||
&DomainName
|
||
)) != NERR_Success) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If local device is a NULL string, it will be treated as a pointer to
|
||
// NULL.
|
||
//
|
||
if ((UseInfo->ui2_local != NULL) &&
|
||
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
|
||
|
||
//
|
||
// Local device name is not NULL, canonicalize it
|
||
//
|
||
if (WsUseCheckLocal(
|
||
UseInfo->ui2_local,
|
||
Local,
|
||
&LocalLength
|
||
) != NERR_Success) {
|
||
(void) LocalFree(UncName);
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check the format of the shared resource name.
|
||
//
|
||
if (WsUseCheckRemote(
|
||
UseInfo->ui2_remote,
|
||
UncName,
|
||
&UncNameLength
|
||
) != NERR_Success) {
|
||
(void) LocalFree(UncName);
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_REMOTE_PARMNUM);
|
||
}
|
||
|
||
if ((Level >= 2) &&
|
||
(UseInfo->ui2_password != NULL) &&
|
||
(UseInfo->ui2_password[0] == TCHAR_EOS) &&
|
||
(UseInfo->ui2_username != NULL) &&
|
||
(UseInfo->ui2_username[0] == TCHAR_EOS) &&
|
||
(UseInfo->ui2_domainname != NULL) &&
|
||
(UseInfo->ui2_domainname[0] == TCHAR_EOS)) {
|
||
|
||
//
|
||
// The user explicitly specified an empty password, username, and
|
||
// domain. This means they want a null session.
|
||
//
|
||
|
||
*UserName = TCHAR_EOS;
|
||
*DomainName = TCHAR_EOS;
|
||
Password = TEXT("");
|
||
|
||
} else {
|
||
|
||
//
|
||
// Canonicalize user and domain names.
|
||
//
|
||
|
||
if (UserName != NULL) {
|
||
|
||
//
|
||
// Canonicalize username
|
||
//
|
||
if ((status = I_NetNameCanonicalize(
|
||
NULL,
|
||
UseInfo->ui2_username,
|
||
UserName,
|
||
(UNLEN + 1) * sizeof(TCHAR),
|
||
NAMETYPE_USER,
|
||
0
|
||
)) != NERR_Success) {
|
||
(void) LocalFree(UncName);
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_USERNAME_PARMNUM);
|
||
}
|
||
}
|
||
|
||
if ( (DomainName != NULL)
|
||
&& (UseInfo->ui2_domainname[0] != TCHAR_EOS) ) {
|
||
|
||
// Must now allow null string for domain name to support UPNs
|
||
// which contain the domain name in the username.
|
||
//
|
||
// Canonicalize domain name
|
||
// Canonicalize as a computername since a computername can be
|
||
// a valid domain (on the workstation to which you are connecting.
|
||
// This allows computernames with spaces to work.
|
||
//
|
||
if ((status = I_NetNameCanonicalize(
|
||
NULL,
|
||
UseInfo->ui2_domainname,
|
||
DomainName,
|
||
(DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR),
|
||
NAMETYPE_COMPUTER,
|
||
0
|
||
)) != NERR_Success) {
|
||
(void) LocalFree(UncName);
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_DOMAINNAME_PARMNUM);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure password length is not too long
|
||
//
|
||
if (UseInfo->ui2_password != NULL) {
|
||
|
||
Password = UseInfo->ui2_password;
|
||
|
||
if (STRLEN(Password) > PWLEN) {
|
||
(void) LocalFree(UncName);
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_PASSWORD_PARMNUM);
|
||
}
|
||
|
||
//
|
||
// Decode the password (the client obfuscated it.)
|
||
//
|
||
|
||
RtlInitUnicodeString( &EncodedPassword, Password );
|
||
|
||
RtlRunDecodeUnicodeString( NETR_USE_ADD_PASSWORD_SEED,
|
||
&EncodedPassword );
|
||
|
||
}
|
||
else {
|
||
Flags |= USE_DEFAULT_CREDENTIALS;
|
||
}
|
||
|
||
}
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseAdd %ws %ws\n", Local, UncName));
|
||
}
|
||
|
||
//
|
||
// Check to see if the format of the local device name is correct based
|
||
// on the shared resource type to be accessed. This function also checks
|
||
// to see if the device is shared.
|
||
//
|
||
if ((status = WsCheckLocalAndDeviceType(
|
||
Local,
|
||
UseInfo->ui2_asg_type,
|
||
ErrorParameter
|
||
)) != NERR_Success) {
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] WsCheckLocalAndDeviceType return %lu\n", status));
|
||
}
|
||
goto FreeWorkBuffer;
|
||
}
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetSessionId(&SessionId)) != NERR_Success) {
|
||
goto FreeWorkBuffer;
|
||
}
|
||
|
||
//
|
||
// Replace \\ with \Device\LanmanRedirector in UncName, and create
|
||
// the NT-style tree connection names (without password or user name)
|
||
//
|
||
if ((status = WsCreateTreeConnectName(
|
||
UncName,
|
||
UncNameLength,
|
||
Local,
|
||
SessionId,
|
||
&TreeConnectStr
|
||
)) != NERR_Success) {
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseAdd Bad tree connect name: %lu\n",
|
||
status));
|
||
}
|
||
goto FreeWorkBuffer;
|
||
}
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
//
|
||
// Don't redirect comm or spooled devices if redirection is paused.
|
||
//
|
||
if( Local != NULL && WsRedirectionPaused(Local) ) {
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseAdd Redirector paused\n"));
|
||
}
|
||
status = ERROR_REDIR_PAUSED;
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
if (Local != NULL) {
|
||
|
||
PUSE_ENTRY UseList;
|
||
DWORD Index;
|
||
|
||
//
|
||
// Lock Use Table so nobody will do anything destructive to it while
|
||
// we're in the middle of all this. If multiple threads are trying
|
||
// to redirect the same drive, only one will succeed creating the
|
||
// symbolic link, and the others will fail.
|
||
//
|
||
|
||
if (! RtlAcquireResourceShared(&Use.TableResource, TRUE)) {
|
||
status = NERR_InternalError;
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
//
|
||
// Look for the matching LogonId in the Use Table, if none matched
|
||
// create a new entry.
|
||
//
|
||
if (WsGetUserEntry(
|
||
&Use,
|
||
&LogonId,
|
||
&Index,
|
||
FALSE
|
||
) != NERR_Success) {
|
||
UseList = NULL;
|
||
}
|
||
else {
|
||
UseList = Use.Table[Index].List;
|
||
}
|
||
|
||
//
|
||
// Create symbolic link for local device name. If there are multiple
|
||
// threads trying to do this, only one will succeed.
|
||
//
|
||
if ((status = WsCreateSymbolicLink(
|
||
Local,
|
||
UseInfo->ui2_asg_type,
|
||
TreeConnectStr.Buffer,
|
||
UseList,
|
||
&Session
|
||
)) != NERR_Success) {
|
||
|
||
if ((ARGUMENT_PRESENT(ErrorParameter)) &&
|
||
(status == ERROR_INVALID_PARAMETER)) {
|
||
*ErrorParameter = USE_LOCAL_PARMNUM;
|
||
}
|
||
}
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
|
||
if( status )
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
//
|
||
// Create the tree connection if none already exists; otherwise, open it.
|
||
//
|
||
status = WsOpenCreateConnection(
|
||
&TreeConnectStr,
|
||
UserName,
|
||
DomainName,
|
||
Password,
|
||
CreateFlags,
|
||
FILE_OPEN_IF,
|
||
UseInfo->ui2_asg_type,
|
||
&TreeConnection,
|
||
NULL
|
||
);
|
||
|
||
if (status != NERR_Success) {
|
||
if (status == NERR_UseNotFound) {
|
||
status = ERROR_DEV_NOT_EXIST;
|
||
}
|
||
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
//
|
||
// Make sure user was correct about the shared resource type
|
||
//
|
||
if ((status = WsCheckEstablishedDeviceType(
|
||
TreeConnection,
|
||
UseInfo->ui2_asg_type
|
||
)) != NERR_Success) {
|
||
WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
|
||
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
|
||
goto FreeAllocatedBuffers;
|
||
}
|
||
|
||
//
|
||
// Add use to the Use Table.
|
||
//
|
||
status = WsAddUse(
|
||
&LogonId,
|
||
TreeConnection,
|
||
Local,
|
||
LocalLength,
|
||
UncName,
|
||
UncNameLength,
|
||
&TreeConnectStr,
|
||
Flags
|
||
);
|
||
|
||
if( status ) {
|
||
WsDeleteConnection(&LogonId, TreeConnection, USE_LOTS_OF_FORCE);
|
||
WsDeleteSymbolicLink( Local, TreeConnectStr.Buffer, Session );
|
||
}
|
||
|
||
FreeAllocatedBuffers:
|
||
//
|
||
// Free tree connection name buffer and work buffer
|
||
//
|
||
(void) LocalFree(TreeConnectStr.Buffer);
|
||
|
||
FreeWorkBuffer:
|
||
(void) LocalFree(UncName);
|
||
(void) LocalFree(Session);
|
||
//
|
||
// Put the password back the way we found it.
|
||
//
|
||
|
||
if ( EncodedPassword.Length != 0 ) {
|
||
UCHAR Seed = NETR_USE_ADD_PASSWORD_SEED;
|
||
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseAdd: about to return status=%lu\n",
|
||
status));
|
||
}
|
||
|
||
|
||
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NET_API_STATUS NET_API_FUNCTION
|
||
NetrUseDel (
|
||
IN LPTSTR ServerName OPTIONAL,
|
||
IN LPTSTR UseName,
|
||
IN DWORD ForceLevel
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is the NetUseDel entry point in the Workstation service.
|
||
|
||
Arguments:
|
||
|
||
UseName - Supplies the local device name or shared resource name of
|
||
the tree connection to be deleted.
|
||
|
||
ForceLevel - Supplies the level of force to delete the tree connection.
|
||
|
||
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
|
||
PUSE_ENTRY BackPointer; // Points to node previous to
|
||
// found use entry
|
||
HANDLE TreeConnection; // Handle to connection
|
||
|
||
TCHAR *FormattedUseName;
|
||
// For canonicalizing a local device
|
||
// name
|
||
DWORD PathType = 0;
|
||
|
||
PUSE_ENTRY UseList;
|
||
|
||
|
||
UNREFERENCED_PARAMETER(ServerName);
|
||
|
||
//
|
||
// Check that ForceLevel parameter is valid
|
||
//
|
||
switch (ForceLevel) {
|
||
|
||
case USE_NOFORCE:
|
||
case USE_LOTS_OF_FORCE:
|
||
case USE_FORCE:
|
||
break;
|
||
|
||
default:
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
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(("\n[Wksta] NetrUseDel %ws %u, formatted use name %ws\n",
|
||
UseName, ForceLevel, FormattedUseName));
|
||
}
|
||
|
||
//
|
||
// Impersonate caller and get the logon id
|
||
//
|
||
if ((status = WsImpersonateAndGetLogonId(&LogonId)) != NERR_Success) {
|
||
LocalFree(FormattedUseName);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Lock Use Table while looking for entry to delete.
|
||
//
|
||
if (! RtlAcquireResourceExclusive(&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,
|
||
&BackPointer
|
||
)) != 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 = WsDeleteConnection(&LogonId, TreeConnection, ForceLevel);
|
||
|
||
//
|
||
// Close the connection handle if the API failed.
|
||
//
|
||
|
||
if (status != NERR_Success) {
|
||
|
||
NtClose(TreeConnection);
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
else if ((MatchedPointer->Local != NULL) &&
|
||
(MatchedPointer->LocalLength > 2)) {
|
||
|
||
//
|
||
// Don't allow delete on comm or spooled devices if redirection is
|
||
// paused for the current user.
|
||
//
|
||
if (WsRedirectionPaused(MatchedPointer->Local)) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
return ERROR_REDIR_PAUSED;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Delete tree connection and remove use entry from Use Table. This function
|
||
// releases the TableResource
|
||
//
|
||
status = WsDeleteUse(
|
||
&LogonId,
|
||
ForceLevel,
|
||
MatchedPointer,
|
||
Index
|
||
);
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("[Wksta] NetrUseDel: about to return status=%lu\n", status));
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsAddUse(
|
||
IN PLUID LogonId,
|
||
IN HANDLE TreeConnection,
|
||
IN LPTSTR Local OPTIONAL,
|
||
IN DWORD LocalLength,
|
||
IN LPTSTR UncName,
|
||
IN DWORD UncNameLength,
|
||
IN PUNICODE_STRING TreeConnectStr,
|
||
IN DWORD Flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds a use (tree connection) entry into the Use Table for
|
||
the user specified by the Logon Id. There is a linked list of uses for
|
||
each user. Each new use entry is inserted into the end of the linked
|
||
list so that enumeration of the list is resumable.
|
||
|
||
NOTE: This function locks the Use Table.
|
||
It also closes the tree connection in case of a error, or if a tree
|
||
connection to the same shared resource already exists.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
TreeConnection - Supplies the handle to the tree connection created.
|
||
|
||
Local - Supplies the string of the local device name.
|
||
|
||
LocalLength - Supplies the length of the local device name.
|
||
|
||
UncName - Supplies the name of the shared resource (UNC name).
|
||
|
||
UncNameLength - Supplies the length of the shared resource.
|
||
|
||
TreeConnectStr - Supplies the string of UNC name in NT-style format
|
||
(\Device\LanmanRedirector\X:\Orville\Razzle).
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS status;
|
||
DWORD Index; // Index to user entry in Use Table
|
||
|
||
PUSE_ENTRY MatchedPointer = NULL; // Points to matching shared resource
|
||
PUSE_ENTRY InsertPointer = NULL; // Point of insertion into use list
|
||
PUSE_ENTRY NewUse; // Pointer to the new use entry
|
||
|
||
if (! RtlAcquireResourceExclusive(&Use.TableResource, TRUE)) {
|
||
(void) NtClose(TreeConnection);
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
//
|
||
// Look for the matching LogonId in the Use Table, if none matched
|
||
// create a new entry.
|
||
//
|
||
if ((status = WsGetUserEntry(
|
||
&Use,
|
||
LogonId,
|
||
&Index,
|
||
TRUE
|
||
)) != NERR_Success) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
(void) NtClose(TreeConnection);
|
||
return status;
|
||
}
|
||
|
||
if (Use.Table[Index].List != NULL) {
|
||
|
||
//
|
||
// Traverse use list to look for location to insert new use entry.
|
||
//
|
||
WsFindInsertLocation(
|
||
(PUSE_ENTRY) Use.Table[Index].List,
|
||
UncName,
|
||
&MatchedPointer,
|
||
&InsertPointer
|
||
);
|
||
}
|
||
|
||
if (MatchedPointer == NULL) {
|
||
|
||
//
|
||
// No matching UNC name found. Create a new entry with a
|
||
// corresponding remote entry.
|
||
//
|
||
if ((status = WsCreateNewEntry(
|
||
&NewUse,
|
||
TreeConnection,
|
||
Local,
|
||
LocalLength,
|
||
UncName,
|
||
UncNameLength,
|
||
TreeConnectStr,
|
||
Flags
|
||
)) != NERR_Success) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
(void) NtClose(TreeConnection);
|
||
return status;
|
||
}
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Matching UNC name found.
|
||
//
|
||
|
||
//
|
||
// It may be unnecessary to create a new use entry if the use
|
||
// we are adding has a NULL local device and a NULL local device
|
||
// entry already exists.
|
||
//
|
||
if (Local == NULL) {
|
||
|
||
if (MatchedPointer->Local == NULL) {
|
||
|
||
//
|
||
// Yes, there is a NULL local device entry already.
|
||
// Increment the use count and we are done.
|
||
//
|
||
MatchedPointer->UseCount++;
|
||
MatchedPointer->Remote->TotalUseCount++;
|
||
|
||
#if DBG
|
||
DumpUseList(Index);
|
||
#endif
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
|
||
//
|
||
// Close newly opened handle to the same tree connection because
|
||
// one already exists.
|
||
//
|
||
(void) NtClose(TreeConnection);
|
||
|
||
return NERR_Success;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we get here means we need to create a new use entry but not
|
||
// a corresponding remote entry because a use with the same UNC
|
||
// name already exists.
|
||
//
|
||
if ((status = WsCreateNewEntry(
|
||
&NewUse,
|
||
TreeConnection,
|
||
Local,
|
||
LocalLength,
|
||
NULL,
|
||
0,
|
||
TreeConnectStr,
|
||
Flags
|
||
)) != NERR_Success) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
(void) NtClose(TreeConnection);
|
||
return status;
|
||
}
|
||
|
||
NewUse->Remote = MatchedPointer->Remote;
|
||
NewUse->Remote->TotalUseCount++;
|
||
}
|
||
|
||
//
|
||
// Insert the new use entry into use list
|
||
//
|
||
if (InsertPointer == NULL) {
|
||
//
|
||
// Inserting into the head of list
|
||
//
|
||
Use.Table[Index].List = (PVOID) NewUse;
|
||
}
|
||
else {
|
||
InsertPointer->Next = NewUse;
|
||
}
|
||
|
||
#if DBG
|
||
DumpUseList(Index);
|
||
#endif
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsDeleteUse(
|
||
IN PLUID LogonId,
|
||
IN DWORD ForceLevel,
|
||
IN PUSE_ENTRY MatchedPointer,
|
||
IN DWORD Index
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function removes the use entry pointed by MatchedPointer and
|
||
free it memory if it is a UNC connection deleted with force, or
|
||
it is a UNC connection deleted with no force and the use count is
|
||
decremented to 0, or it is a connection mapped to a local device.
|
||
|
||
WARNING: This function assumes that the Use.TableResource is claimed.
|
||
And it releases it on exit.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Supplies a pointer to the user's Logon Id.
|
||
|
||
ForceLevel - Supplies the level of force to delete.
|
||
|
||
MatchedPointer - Supplies the pointer to the use entry to be deleted.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PUSE_ENTRY BackPointer;
|
||
NET_API_STATUS status;
|
||
|
||
//
|
||
// No need to remove entry if UNC connection is deleted with USE_NOFORCE
|
||
// level, and use count is not 0 after the deletion.
|
||
//
|
||
if ((MatchedPointer->Local == NULL) &&
|
||
(ForceLevel == USE_NOFORCE) &&
|
||
((MatchedPointer->UseCount - 1) > 0)) {
|
||
|
||
MatchedPointer->UseCount--;
|
||
MatchedPointer->Remote->TotalUseCount--;
|
||
NetpAssert(MatchedPointer->Remote->TotalUseCount);
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
return NERR_Success;
|
||
}
|
||
|
||
//
|
||
// Delete the tree connection and close the handle.
|
||
//
|
||
if ((status = WsDeleteConnection(
|
||
LogonId,
|
||
MatchedPointer->TreeConnection,
|
||
ForceLevel )) != NERR_Success) {
|
||
RtlReleaseResource(&Use.TableResource);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Successfully deleted connection, and refound our entry.
|
||
//
|
||
|
||
BackPointer = (PUSE_ENTRY)Use.Table[Index].List;
|
||
|
||
if (BackPointer != MatchedPointer) {
|
||
while (BackPointer->Next != NULL) {
|
||
if (BackPointer->Next == MatchedPointer) {
|
||
break;
|
||
} else {
|
||
BackPointer = BackPointer->Next;
|
||
}
|
||
}
|
||
|
||
ASSERT(BackPointer->Next == MatchedPointer);
|
||
|
||
BackPointer->Next = MatchedPointer->Next;
|
||
} else {
|
||
//
|
||
// Use entry is the first one on the use list
|
||
//
|
||
Use.Table[Index].List = (PVOID) MatchedPointer->Next;
|
||
}
|
||
|
||
MatchedPointer->Remote->TotalUseCount -= MatchedPointer->UseCount;
|
||
|
||
if (MatchedPointer->Remote->TotalUseCount == 0) {
|
||
(void) LocalFree((HLOCAL) MatchedPointer->Remote);
|
||
}
|
||
|
||
RtlReleaseResource(&Use.TableResource);
|
||
|
||
//
|
||
// Delete symbolic link, if any.
|
||
// Must perform the deletion outside of exclusively holding the
|
||
// Use.TableResource.
|
||
// Otherwise, when the shell tries to update the current status of
|
||
// a drive letter change, the explorer.exe thread will block while
|
||
// trying to acquire the Use.TableResource
|
||
//
|
||
WsDeleteSymbolicLink(
|
||
MatchedPointer->Local,
|
||
MatchedPointer->TreeConnectStr,
|
||
NULL
|
||
);
|
||
|
||
(void) LocalFree((HLOCAL) MatchedPointer);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCreateNewEntry(
|
||
OUT PUSE_ENTRY *NewUse,
|
||
IN HANDLE TreeConnection,
|
||
IN LPTSTR Local OPTIONAL,
|
||
IN DWORD LocalLength,
|
||
IN LPTSTR UncName OPTIONAL,
|
||
IN DWORD UncNameLength,
|
||
IN PUNICODE_STRING TreeConnectStr,
|
||
IN DWORD Flags
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function creates and initializes a new use entry. If the UncName
|
||
is specified, a new remote entry is created and initialized with
|
||
UncName.
|
||
|
||
Arguments:
|
||
|
||
NewUse - Returns a pointer to the newly allocated and initialized use
|
||
entry.
|
||
|
||
TreeConnection - Supplies the handle to the tree connection to set in
|
||
the new use entry.
|
||
|
||
Local - Supplies the local device name string to be copied into the new
|
||
use entry.
|
||
|
||
LocalLength - Supplies the length of the local device name string.
|
||
|
||
UncName - Supplies the UNC name string to be copied into the new use entry.
|
||
|
||
UncNameLength - Supplies the length of the UNC name string.
|
||
|
||
TreeConnectStr - Supplies the string of UNC name in NT-style format
|
||
(\Device\LanmanRedirector\X:\Orville\Razzle).
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
PUNC_NAME NewRemoteEntry; // Common extension to use entries which
|
||
// share the same UNC connection.
|
||
|
||
|
||
//
|
||
// Allocate memory for new use. String length does not include zero
|
||
// terminator so add that.
|
||
//
|
||
if ((*NewUse = (PUSE_ENTRY) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
ROUND_UP_COUNT(
|
||
sizeof(USE_ENTRY) + (LocalLength + 1)
|
||
* sizeof(TCHAR),
|
||
ALIGN_WCHAR
|
||
) +
|
||
(ARGUMENT_PRESENT(Local) ?
|
||
TreeConnectStr->MaximumLength :
|
||
0
|
||
)
|
||
)) == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
//
|
||
// Put use information into the new use node
|
||
//
|
||
(*NewUse)->Next = NULL;
|
||
(*NewUse)->LocalLength = LocalLength;
|
||
(*NewUse)->UseCount = 1;
|
||
(*NewUse)->TreeConnection = TreeConnection;
|
||
(*NewUse)->ResumeKey = GlobalResumeKey++;
|
||
(*NewUse)->Flags = Flags;
|
||
|
||
//
|
||
// GlobalResumeKey wraps back to 0 if it is 0x80000000 because we use the
|
||
// high bit to indicate whether the resume handle for NetUseEnum comes
|
||
// from the workstation service or from the redirector.
|
||
//
|
||
GlobalResumeKey &= ~(REDIR_LIST);
|
||
|
||
//
|
||
// Copy local device name into use entry after the LocalLength field,
|
||
// if it is specified.
|
||
//
|
||
if (ARGUMENT_PRESENT(Local)) {
|
||
(*NewUse)->Local = (LPTSTR) ((DWORD_PTR) *NewUse + sizeof(USE_ENTRY));
|
||
STRCPY((*NewUse)->Local, Local);
|
||
|
||
(*NewUse)->TreeConnectStr = (LPWSTR) ROUND_UP_COUNT(
|
||
((DWORD_PTR) *NewUse +
|
||
sizeof(USE_ENTRY) +
|
||
(LocalLength + 1) *
|
||
sizeof(TCHAR)),
|
||
ALIGN_WCHAR
|
||
);
|
||
|
||
wcscpy((*NewUse)->TreeConnectStr, TreeConnectStr->Buffer);
|
||
}
|
||
else {
|
||
(*NewUse)->Local = NULL;
|
||
(*NewUse)->TreeConnectStr = NULL;
|
||
}
|
||
|
||
//
|
||
// If shared resource name is specified, create a new remote entry to hold
|
||
// the UNC name, the tree connection handle, and total number of uses on
|
||
// this shared resource.
|
||
//
|
||
if (ARGUMENT_PRESENT(UncName)) {
|
||
|
||
if ((NewRemoteEntry = (PUNC_NAME) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) (sizeof(UNC_NAME) +
|
||
UncNameLength * sizeof(TCHAR))
|
||
)) == NULL) {
|
||
(void) LocalFree((HLOCAL) *NewUse);
|
||
return GetLastError();
|
||
}
|
||
|
||
STRCPY((LPWSTR) NewRemoteEntry->UncName, UncName);
|
||
NewRemoteEntry->UncNameLength = UncNameLength;
|
||
NewRemoteEntry->TotalUseCount = 1;
|
||
// NewRemoteEntry->RedirUseInfo = NULL;
|
||
|
||
(*NewUse)->Remote = NewRemoteEntry;
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCheckLocalAndDeviceType(
|
||
IN OUT LPTSTR Local,
|
||
IN DWORD DeviceType,
|
||
OUT LPDWORD ErrorParameter OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks the format of the specified local device name
|
||
based on the device type of shared resource to be accessed, and at
|
||
the same time verifies that the device type is valid.
|
||
|
||
Arguments:
|
||
|
||
Local - Supplies the local device name. Returns its canonicalized
|
||
form.
|
||
|
||
DeviceType - Supplies the shared resource device type.
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Validate local device name based on the shared resource type.
|
||
//
|
||
|
||
//
|
||
// Check for wild card device type outside of the switch statement
|
||
// below because compiler complains about constant too big.
|
||
//
|
||
if (DeviceType == USE_WILDCARD || DeviceType == USE_IPC) {
|
||
|
||
//
|
||
// Local device name must be NULL for wild card or IPC connection.
|
||
//
|
||
if (Local == NULL) {
|
||
return NERR_Success;
|
||
}
|
||
else {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
|
||
}
|
||
}
|
||
|
||
switch (DeviceType) {
|
||
|
||
case USE_DISKDEV:
|
||
|
||
if (Local == NULL) {
|
||
return NERR_Success;
|
||
}
|
||
|
||
//
|
||
// Local device name must have "<drive>:" format for disk
|
||
// device.
|
||
//
|
||
if (STRLEN(Local) != 2 || Local[1] != TCHAR_COLON) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
|
||
}
|
||
|
||
break;
|
||
|
||
case USE_SPOOLDEV:
|
||
|
||
if (Local == NULL) {
|
||
return NERR_Success;
|
||
}
|
||
|
||
//
|
||
// Local device name must have "LPTn:" or "PRN:" format
|
||
// for a print device.
|
||
//
|
||
if ((STRNICMP(Local, TEXT("PRN"), 3) != 0) &&
|
||
(STRNICMP(Local, TEXT("LPT"), 3) != 0)) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
|
||
}
|
||
break;
|
||
|
||
case USE_CHARDEV:
|
||
|
||
if (Local == NULL) {
|
||
return NERR_Success;
|
||
}
|
||
|
||
//
|
||
// Local device name must have "COMn:" or "AUX:" format
|
||
// for a comm device.
|
||
//
|
||
if ((STRNICMP(Local, TEXT("AUX"), 3) != 0) &&
|
||
(STRNICMP(Local, TEXT("COM"), 3) != 0)) {
|
||
RETURN_INVALID_PARAMETER(ErrorParameter, USE_LOCAL_PARMNUM);
|
||
}
|
||
break;
|
||
|
||
|
||
default:
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint((
|
||
"[Wksta] NetrUseAdd: Unknown shared resource type %lu\n",
|
||
DeviceType));
|
||
}
|
||
|
||
return NERR_BadAsgType;
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsCheckEstablishedDeviceType(
|
||
IN HANDLE TreeConnection,
|
||
IN DWORD RequestedDeviceType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function verifies that the device type of the shared resource we
|
||
have connected to is the same as the requested device type.
|
||
|
||
Arguments:
|
||
|
||
TreeConnection - Supplies handle to established tree connection.
|
||
|
||
RequestedDeviceType - Supplies the shared resource device type specified
|
||
by the user to create the tree connection.
|
||
|
||
Return Value:
|
||
|
||
NET_API_STATUS - NERR_Success or reason for failure.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntstatus;
|
||
FILE_FS_DEVICE_INFORMATION FileInformation;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
|
||
ntstatus = NtQueryVolumeInformationFile(
|
||
TreeConnection,
|
||
&IoStatusBlock,
|
||
(PVOID) &FileInformation,
|
||
sizeof(FILE_FS_DEVICE_INFORMATION),
|
||
FileFsDeviceInformation
|
||
);
|
||
|
||
if (! NT_SUCCESS(ntstatus) || ! NT_SUCCESS(IoStatusBlock.Status)) {
|
||
return NERR_InternalError;
|
||
}
|
||
|
||
//
|
||
// Check for wild card device type outside of the switch statement
|
||
// below because compiler complains about constant too big.
|
||
//
|
||
if (RequestedDeviceType == USE_WILDCARD) {
|
||
return NERR_Success;
|
||
}
|
||
|
||
switch (RequestedDeviceType) {
|
||
case USE_DISKDEV:
|
||
if (FileInformation.DeviceType != FILE_DEVICE_DISK) {
|
||
return ERROR_BAD_DEV_TYPE;
|
||
}
|
||
break;
|
||
|
||
case USE_SPOOLDEV:
|
||
if (FileInformation.DeviceType != FILE_DEVICE_PRINTER) {
|
||
return ERROR_BAD_DEV_TYPE;
|
||
}
|
||
break;
|
||
|
||
case USE_CHARDEV:
|
||
if (FileInformation.DeviceType != FILE_DEVICE_SERIAL_PORT) {
|
||
return ERROR_BAD_DEV_TYPE;
|
||
}
|
||
break;
|
||
|
||
case USE_IPC:
|
||
if (FileInformation.DeviceType != FILE_DEVICE_NAMED_PIPE) {
|
||
return ERROR_BAD_DEV_TYPE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// This should have been error checked earlier.
|
||
//
|
||
NetpKdPrint((
|
||
"WsCheckEstablishedDeviceType: Unknown device type.\n"
|
||
));
|
||
NetpAssert(FALSE);
|
||
return ERROR_BAD_DEV_TYPE;
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
STATIC
|
||
NET_API_STATUS
|
||
WsAllocateUseWorkBuffer(
|
||
IN PUSE_INFO_2 UseInfo,
|
||
IN DWORD Level,
|
||
OUT LPTSTR *UncName,
|
||
OUT LPTSTR *Local,
|
||
OUT LPTSTR *UserName,
|
||
OUT LPTSTR *DomainName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates the work buffer for NetrUseAdd. The buffer
|
||
is the maximum need for canonicalizing and storing the strings
|
||
described below. If any of the strings is NULL, no memory is allocated
|
||
for it.
|
||
|
||
UncName - UNC name of remote resource. Cannot be NULL.
|
||
|
||
Local - local device name specified in the NetUseAdd. May be NULL.
|
||
|
||
UserName - username to establish connection with. May be NULL.
|
||
|
||
DomainName - domain name. Must be specified if UserName is,
|
||
otherwise if UserName is NULL this string is ignored.
|
||
|
||
|
||
Arguments:
|
||
|
||
UseInfo - Supplies the input structure for NetUseAdd.
|
||
|
||
Level - Supplies the use info level.
|
||
|
||
Output pointers are set to point into allocated work buffer if its
|
||
corresponding input string is not NULL or empty.
|
||
|
||
Return Value:
|
||
|
||
Error from LocalAlloc.
|
||
|
||
--*/
|
||
{
|
||
DWORD WorkBufferSize = (MAX_PATH + 1) * sizeof(TCHAR);
|
||
LPBYTE WorkBuffer;
|
||
|
||
|
||
if ((UseInfo->ui2_local != NULL) &&
|
||
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
|
||
WorkBufferSize += (DEVLEN + 1) * sizeof(TCHAR);
|
||
}
|
||
|
||
if (Level >= 2) {
|
||
if (UseInfo->ui2_username != NULL) {
|
||
WorkBufferSize += (UNLEN + 1) * sizeof(TCHAR);
|
||
}
|
||
|
||
if (UseInfo->ui2_domainname != NULL) {
|
||
WorkBufferSize += (DNS_MAX_NAME_LENGTH + 1) * sizeof(TCHAR);
|
||
}
|
||
}
|
||
|
||
|
||
if ((WorkBuffer = (LPBYTE) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) WorkBufferSize
|
||
)) == NULL) {
|
||
return GetLastError();
|
||
}
|
||
|
||
*UncName = (LPTSTR) WorkBuffer;
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint((" Remote x%08lx\n", *UncName));
|
||
}
|
||
|
||
WorkBuffer += (MAX_PATH + 1) * sizeof(TCHAR);
|
||
|
||
if ((UseInfo->ui2_local != NULL) &&
|
||
(UseInfo->ui2_local[0] != TCHAR_EOS)) {
|
||
*Local = (LPTSTR) WorkBuffer;
|
||
WorkBuffer += (DEVLEN + 1) * sizeof(TCHAR);
|
||
}
|
||
else {
|
||
*Local = NULL;
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint((" Local x%08lx\n", *Local));
|
||
}
|
||
|
||
|
||
if (Level >= 2) {
|
||
|
||
if (UseInfo->ui2_username != NULL) {
|
||
*UserName = (LPTSTR) WorkBuffer;
|
||
WorkBuffer += (UNLEN + 1) * sizeof(TCHAR);
|
||
}
|
||
else {
|
||
*UserName = NULL;
|
||
}
|
||
|
||
if (UseInfo->ui2_domainname != NULL) {
|
||
*DomainName = (LPTSTR) WorkBuffer;
|
||
}
|
||
else {
|
||
*DomainName = NULL;
|
||
}
|
||
}
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint((" UserName x%08lx, DomainName x%08lx\n",
|
||
*UserName, *DomainName));
|
||
}
|
||
|
||
return NERR_Success;
|
||
}
|
||
|
||
|
||
#if DBG
|
||
|
||
STATIC
|
||
VOID
|
||
DumpUseList(
|
||
DWORD Index
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function dumps the user's use list for debugging purposes.
|
||
|
||
Arguments:
|
||
|
||
Index - Supplies the index to the user entry in the Use Table.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PUSE_ENTRY UseList = (PUSE_ENTRY) Use.Table[Index].List;
|
||
|
||
IF_DEBUG(USE) {
|
||
NetpKdPrint(("\nDump Use List @%08lx\n", UseList));
|
||
|
||
while (UseList != NULL) {
|
||
NetpKdPrint(("%ws %ws\n", UseList->Local,
|
||
UseList->Remote->UncName));
|
||
|
||
NetpKdPrint(("usecount=%lu, totalusecount=%lu\n",
|
||
UseList->UseCount, UseList->Remote->TotalUseCount));
|
||
NetpKdPrint(("Connection handle %08lx, resume key=%lu\n",
|
||
UseList->TreeConnection, UseList->ResumeKey));
|
||
|
||
UseList = UseList->Next;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|