Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7723 lines
228 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
enum.c
Abstract:
This module contains server, volume, and directory enumeration
routines supported by NetWare Workstation service.
Author:
Rita Wong (ritaw) 15-Feb-1993
Revision History:
--*/
#include <stdlib.h>
#include <nw.h>
#include <splutil.h>
#include <nwmisc.h>
#include <nwreg.h>
#include <nds.h>
#include <nwapi32.h>
VOID
GetLuid(
IN OUT PLUID plogonid
);
//-------------------------------------------------------------------//
// //
// Definitions //
// //
//-------------------------------------------------------------------//
//
// Other definitions
//
#define ONE_KB 1024
#define TWO_KB 2048
#define FOUR_KB 4096
#define EIGHT_KB 8192
#define TREECHAR L'*'
#define NW_VOLUME_NAME_LEN 256
#define NW_MAX_VOLUME_NUMBER 64
//
// This structure is orginally defined in nwapi32.c, it is redefined
// here so that the routine NWGetFileServerVersionInfo() can be called
// with it.
//
typedef struct _NWC_SERVER_INFO {
HANDLE hConn ;
UNICODE_STRING ServerString ;
} NWC_SERVER_INFO ;
//-------------------------------------------------------------------//
// //
// Local Function Prototypes //
// //
//-------------------------------------------------------------------//
DWORD
NwrOpenEnumServersCommon(
IN NW_ENUM_TYPE EnumType,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
);
DWORD
NwrOpenEnumCommon(
IN LPWSTR ContainerName,
IN NW_ENUM_TYPE EnumType,
IN DWORD_PTR StartingPoint,
IN BOOL ValidateUserFlag,
IN LPWSTR UserName OPTIONAL,
IN LPWSTR Password OPTIONAL,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
OUT LPDWORD ClassTypeOfNDSLeaf,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
);
DWORD
NwEnumContextInfo(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumServersAndNdsTrees(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumPrintServers(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumVolumes(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumNdsSubTrees_Disk(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumNdsSubTrees_Print(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumNdsSubTrees_Any(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumVolumesQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumDirectories(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwEnumPrintQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
);
DWORD
NwGetFirstDirectoryEntry(
IN HANDLE DirHandle,
OUT LPWSTR *DirEntry
);
DWORD
NwGetNextDirectoryEntry(
IN HANDLE DirHandle,
OUT LPWSTR *DirEntry
);
DWORD
NwGetFirstNdsSubTreeEntry(
OUT LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD BufferSize
);
DWORD
NwGetNextNdsSubTreeEntry(
OUT LPNW_ENUM_CONTEXT ContextHandle
);
BYTE
NwGetSubTreeData(
IN DWORD_PTR NdsRawDataPtr,
OUT LPWSTR * SubTreeName,
OUT LPDWORD ResourceScope,
OUT LPDWORD ResourceType,
OUT LPDWORD ResourceDisplayType,
OUT LPDWORD ResourceUsage,
OUT LPWSTR * StrippedObjectName
);
VOID
NwStripNdsUncName(
IN LPWSTR ObjectName,
OUT LPWSTR * StrippedObjectName
);
#define VERIFY_ERROR_NOT_A_NDS_TREE 0x1010FFF0
#define VERIFY_ERROR_PATH_NOT_FOUND 0x1010FFF1
DWORD
NwVerifyNDSObject(
IN LPWSTR lpNDSObjectNamePath,
OUT LPWSTR * lpFullNDSObjectNamePath,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
);
DWORD
NwVerifyBinderyObject(
IN LPWSTR lpBinderyObjectNamePath,
OUT LPWSTR * lpFullBinderyObjectNamePath,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
);
DWORD
NwGetNDSPathInfo(
IN LPWSTR lpNDSObjectNamePath,
OUT LPWSTR * lpSystemObjectNamePath,
OUT LPWSTR * lpSystemPathPart,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
);
DWORD
NwGetBinderyPathInfo(
IN LPWSTR lpBinderyObjectNamePath,
OUT LPWSTR * lpSystemObjectNamePath,
OUT LPWSTR * lpSystemPathPart,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
);
BOOL
NwGetRemoteNameParent(
IN LPWSTR lpRemoteName,
OUT LPWSTR * lpRemoteNameParent
);
int __cdecl
SortFunc(
IN CONST VOID *p1,
IN CONST VOID *p2
);
DWORD
NwGetConnectionInformation(
IN LPWSTR lpName,
OUT LPWSTR lpUserName,
OUT LPWSTR lpHostServer
);
VOID
NwpGetUncInfo(
IN LPWSTR lpstrUnc,
OUT WORD * slashCount,
OUT BOOL * isNdsUnc,
OUT LPWSTR * FourthSlash
);
DWORD
NwpGetCurrentUserRegKey(
IN DWORD DesiredAccess,
OUT HKEY *phKeyCurrentUser
);
DWORD
NwQueryInfo(
OUT LPWSTR *ppszPreferredSrv
);
DWORD
NwrOpenEnumContextInfo(
IN LPWSTR Reserved OPTIONAL,
IN DWORD ConnectionType,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function creates a new context handle and initializes it
for enumerating context information (i.e. NDS user context objects
and/or NetWare bindery server connections).
Arguments:
Reserved - Unused.
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
--*/
{
LPWSTR pszCurrentContext = NULL;
DWORD dwPrintOptions;
DWORD status = NwQueryInfo( &pszCurrentContext );
WCHAR Context[MAX_NDS_NAME_CHARS];
LPNW_ENUM_CONTEXT ContextHandle;
UNREFERENCED_PARAMETER(Reserved);
UNREFERENCED_PARAMETER(ConnectionType);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumContextInfo\n"));
}
#endif
if ( pszCurrentContext &&
status == NO_ERROR )
{
if ( pszCurrentContext[0] == TREECHAR )
{
wcscpy( Context, L"\\\\" );
wcscat( Context, pszCurrentContext + 1 );
LocalFree( pszCurrentContext );
pszCurrentContext = NULL;
return NwrOpenEnumCommon(
Context,
NwsHandleListContextInfo_Tree,
(DWORD_PTR) -1,
FALSE,
NULL,
NULL,
0,
0,
NULL,
EnumHandle
);
}
else
{
//
// The user does not have a preferred NDS tree and context. They
// may have only a preferred server.
//
if ( pszCurrentContext[0] != 0 )
{
//
// There is a prefered server.
//
LocalFree( pszCurrentContext );
pszCurrentContext = NULL;
ContextHandle = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(NW_ENUM_CONTEXT)
);
if (ContextHandle == NULL)
{
KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize contents of the context handle structure.
//
ContextHandle->Signature = NW_HANDLE_SIGNATURE;
ContextHandle->HandleType = NwsHandleListContextInfo_Server;
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
ContextHandle->ResumeId = (DWORD_PTR) -1;
// The following are set to zero due to the LMEM_ZEROINIT.
// ContextHandle->NdsRawDataBuffer = 0;
// ContextHandle->NdsRawDataSize = 0;
// ContextHandle->NdsRawDataId = 0;
// ContextHandle->NdsRawDataCount = 0;
// ContextHandle->TreeConnectionHandle = 0;
// ContextHandle->ConnectionType = 0;
//
// Return the newly created context.
//
*EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
return NO_ERROR;
}
}
}
//
// There is no information in the registry about the current user.
// We go ahead and make an enumeration handle and return success.
// Later, during a call to NPEnumResource, we will return zero items.
// This is done because there is no valid return code to tell the
// callee that we have no context information to provide.
//
ContextHandle = (PVOID) LocalAlloc( LMEM_ZEROINIT,
sizeof(NW_ENUM_CONTEXT) );
if (ContextHandle == NULL)
{
KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize contents of the context handle structure.
//
ContextHandle->Signature = NW_HANDLE_SIGNATURE;
ContextHandle->HandleType = NwsHandleListContextInfo_Server;
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
ContextHandle->ResumeId = 0; // This will tell NwrEnum to
// give up (i.e. we are done).
// The following are set to zero due to the LMEM_ZEROINIT.
// ContextHandle->NdsRawDataBuffer = 0;
// ContextHandle->NdsRawDataSize = 0;
// ContextHandle->NdsRawDataId = 0;
// ContextHandle->NdsRawDataCount = 0;
// ContextHandle->TreeConnectionHandle = 0;
// ContextHandle->ConnectionType = 0;
//
// Return the newly created context.
//
*EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
return NO_ERROR;
}
DWORD
NwrOpenEnumServersAndNdsTrees(
IN LPWSTR Reserved OPTIONAL,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function creates a new context handle and initializes it
for enumerating the servers and NDS trees on the network.
Arguments:
Reserved - Unused.
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
--*/ // NwrOpenEnumServersAndNdsTrees
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint( ("\nNWWORKSTATION: NwrOpenEnumServersAndNdsTrees\n") );
}
#endif
return NwrOpenEnumServersCommon(
NwsHandleListServersAndNdsTrees,
EnumHandle
);
}
DWORD
NwOpenEnumPrintServers(
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function creates a new context handle and initializes it
for enumerating the print servers on the network.
Arguments:
Reserved - Unused.
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
--*/ // NwOpenEnumPrintServers
{
#if DBG
IF_DEBUG(ENUM) {
KdPrint( ("\nNWWORKSTATION: NwOpenEnumPrintServers\n") );
}
#endif
return NwrOpenEnumServersCommon(
NwsHandleListPrintServers,
EnumHandle
);
}
DWORD
NwrOpenEnumVolumes(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ServerName,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the volumes on a server.
Arguments:
Reserved - Unused.
ServerName - Supplies the name of the server to enumerate volumes.
This name is prefixed by \\.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumVolumes
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumes %ws\n",
ServerName));
}
#endif
return NwrOpenEnumCommon(
ServerName,
NwsHandleListVolumes,
0,
FALSE,
NULL,
NULL,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
EnumHandle
);
}
DWORD
NwrOpenEnumNdsSubTrees_Disk(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ParentPathName,
OUT LPDWORD ClassTypeOfNDSLeaf,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the DISK object types
and containers of a sub-tree in a NDS tree.
Arguments:
Reserved - Unused.
ParentPathName - Supplies the name of the tree and the path to a container
to enumerate sub-trees.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumNdsSubTrees_Disk
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Disk %ws\n",
ParentPathName));
}
#endif
return NwrOpenEnumCommon(
ParentPathName,
NwsHandleListNdsSubTrees_Disk,
0,
FALSE,
NULL,
NULL,
0,
0,
ClassTypeOfNDSLeaf,
EnumHandle
);
}
DWORD
NwrOpenEnumNdsSubTrees_Print(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ParentPathName,
OUT LPDWORD ClassTypeOfNDSLeaf,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the PRINT object types
and containers of a sub-tree in a NDS tree.
Arguments:
Reserved - Unused.
ParentPathName - Supplies the name of the tree and the path to a container
to enumerate sub-trees.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumNdsSubTrees_Print
{
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Print %ws\n",
ParentPathName));
}
#endif
return NwrOpenEnumCommon(
ParentPathName,
NwsHandleListNdsSubTrees_Print,
0,
FALSE,
NULL,
NULL,
0,
0,
ClassTypeOfNDSLeaf,
EnumHandle
);
}
DWORD
NwrOpenEnumNdsSubTrees_Any(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ParentPathName,
OUT LPDWORD ClassTypeOfNDSLeaf,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the ANY object types
and containers of a sub-tree in a NDS tree.
Arguments:
Reserved - Unused.
ParentPathName - Supplies the name of the tree and the path to a container
to enumerate sub-trees.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumNdsSubTrees_Any
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Any %ws\n",
ParentPathName));
}
#endif
return NwrOpenEnumCommon(
ParentPathName,
NwsHandleListNdsSubTrees_Any,
0,
FALSE,
NULL,
NULL,
0,
0,
ClassTypeOfNDSLeaf,
EnumHandle
);
}
DWORD
NwrOpenEnumQueues(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ServerName,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the volumes on a server.
Arguments:
Reserved - Unused.
ServerName - Supplies the name of the server to enumerate volumes.
This name is prefixed by \\.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumQueues
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumQueues %ws\n",
ServerName));
}
#endif
return NwrOpenEnumCommon(
ServerName,
NwsHandleListQueues,
(DWORD_PTR) -1,
TRUE,
NULL,
NULL,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
EnumHandle
);
}
DWORD
NwrOpenEnumVolumesQueues(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ServerName,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the volumes/queues on a server.
Arguments:
Reserved - Unused.
ServerName - Supplies the name of the server to enumerate volumes.
This name is prefixed by \\.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwrOpenEnumVolumesQueues
{
DWORD status;
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumesQueues %ws\n",
ServerName));
}
#endif
status = NwrOpenEnumCommon(
ServerName,
NwsHandleListVolumesQueues,
0,
FALSE,
NULL,
NULL,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
EnumHandle
);
if ( status == NO_ERROR )
((LPNW_ENUM_CONTEXT) *EnumHandle)->ConnectionType = CONNTYPE_DISK;
return status;
}
DWORD
NwrOpenEnumDirectories(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR ParentPathName,
IN LPWSTR UserName OPTIONAL,
IN LPWSTR Password OPTIONAL,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the volumes on a server.
Arguments:
Reserved - Unused.
ParentPathName - Supplies the parent path name in the format of
\\Server\Volume.
UserName - Supplies the username to connect with.
Password - Supplies the password to connect with.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ //NwrOpenEnumDirectories
{
UNREFERENCED_PARAMETER(Reserved);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwrOpenEnumDirectories %ws\n",
ParentPathName));
}
#endif
return NwrOpenEnumCommon(
ParentPathName,
NwsHandleListDirectories,
0,
FALSE,
UserName,
Password,
FILE_CREATE,
FILE_CREATE_TREE_CONNECTION |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
EnumHandle
);
}
DWORD
NwOpenEnumPrintQueues(
IN LPWSTR ServerName,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function calls a common routine which creates a new context
handle and initializes it for enumerating the print queues on a server.
Arguments:
Reserved - Unused.
ServerName - Supplies the name of the server to enumerate volumes.
This name is prefixed by \\.
EnumHandle - Receives the newly created context handle.
Return Value:
NO_ERROR or reason for failure.
--*/ // NwOpenEnumPrintQueues
{
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("\nNWWORKSTATION: NwOpenEnumPrintQueues %ws\n",
ServerName));
}
#endif
return NwrOpenEnumCommon(
ServerName,
NwsHandleListPrintQueues,
(DWORD_PTR) -1,
TRUE,
NULL,
NULL,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
EnumHandle
);
}
DWORD
NwrOpenEnumServersCommon(
IN NW_ENUM_TYPE EnumType,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function creates a new context handle and initializes it
for enumerating the servers on the network.
Arguments:
EnumType - Supplies the type of the object we want to enumerate
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
--*/ // NwrOpenEnumServersCommon
{
DWORD status = NO_ERROR;
LPNW_ENUM_CONTEXT ContextHandle = NULL;
//
// Allocate memory for the context handle structure.
//
ContextHandle = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(NW_ENUM_CONTEXT)
);
if (ContextHandle == NULL) {
KdPrint((
"NWWORKSTATION: NwrOpenEnumServersCommon LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize contents of the context handle structure.
//
ContextHandle->Signature = NW_HANDLE_SIGNATURE;
ContextHandle->HandleType = EnumType;
ContextHandle->ResumeId = (DWORD_PTR) -1;
ContextHandle->NdsRawDataBuffer = 0x00000000;
ContextHandle->NdsRawDataSize = 0x00000000;
ContextHandle->NdsRawDataId = 0x00000000;
ContextHandle->NdsRawDataCount = 0x00000000;
//
// Set flag to indicate that we are going to enumerate NDS trees first.
//
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
//
// Impersonate the client
//
if ((status = NwImpersonateClient()) != NO_ERROR)
{
goto CleanExit;
}
//
// We enum servers and nds trees from the preferred server.
//
status = NwOpenPreferredServer(
&ContextHandle->TreeConnectionHandle
);
(void) NwRevertToSelf() ;
if (status == NO_ERROR)
{
//
// Return the newly created context.
//
*EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
return status;
}
CleanExit:
if ( ContextHandle )
{
ContextHandle->Signature = 0x0BADBAD0;
(void) LocalFree((HLOCAL) ContextHandle);
}
return status;
}
DWORD
NwrOpenEnumCommon(
IN LPWSTR ContainerName,
IN NW_ENUM_TYPE EnumType,
IN DWORD_PTR StartingPoint,
IN BOOL ValidateUserFlag,
IN LPWSTR UserName OPTIONAL,
IN LPWSTR Password OPTIONAL,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
OUT LPDWORD ClassTypeOfNDSLeaf,
OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function is common code for creating a new context handle
and initializing it for enumerating either volumes, directories,
or NDS subtrees.
Arguments:
ContainerName - Supplies the full path name to the container object
we are enumerating from.
EnumType - Supplies the type of the object we want to enumerate
StartingPoint - Supplies the initial resume ID.
UserName - Supplies the username to connect with.
Password - Supplies the password to connect with.
EnumHandle - Receives the newly created context handle.
Return Value:
ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
not be allocated.
NO_ERROR - Call was successful.
Other errors from failure to open a handle to the server.
--*/ // NwrOpenEnumCommon
{
DWORD status = NO_ERROR;
NTSTATUS ntstatus = STATUS_SUCCESS;
LPNW_ENUM_CONTEXT ContextHandle = NULL;
LPWSTR StrippedContainerName = NULL;
BOOL fImpersonate = FALSE ;
if ( ClassTypeOfNDSLeaf )
*ClassTypeOfNDSLeaf = 0;
//
// Before we do anything, we need to convert the UNC passed to
// us. We need to get rid of any CN=XXX.OU=YYY.O=ZZZ references, and
// convert them to XXX.YYY.ZZZ format. Any NETRESOURCE that we generate
// will look like \\TREE\XXX.YYY.ZZZ for a NDS Unc. We do this to
// work around to a bug in WOW.EXE, that prevents 16 bit apps from
// being launched when the user types NDS paths with the CN= stuff in it.
//
NwStripNdsUncName( ContainerName, &StrippedContainerName );
if ( StrippedContainerName == NULL )
{
KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Allocate memory for the context handle structure and space for
// the ContainerName plus \. Now need one more for NULL terminator
// because it's already included in the structure.
//
ContextHandle = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(NW_ENUM_CONTEXT) +
(wcslen(StrippedContainerName) + 1) * sizeof(WCHAR)
);
if (ContextHandle == NULL)
{
if ( StrippedContainerName )
{
(void) LocalFree((HLOCAL) StrippedContainerName);
StrippedContainerName = NULL;
}
KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Initialize contents of the context handle structure.
//
ContextHandle->Signature = NW_HANDLE_SIGNATURE;
ContextHandle->HandleType = EnumType;
ContextHandle->ResumeId = StartingPoint;
//
// These are set to zero due to LMEM_ZEROINIT.
//
// ContextHandle->NdsRawDataBuffer = 0;
// ContextHandle->NdsRawDataSize = 0;
// ContextHandle->NdsRawDataId = 0;
// ContextHandle->NdsRawDataCount = 0;
// ContextHandle->TreeConnectionHandle = 0;
//
// Impersonate the client
//
if ( ( status = NwImpersonateClient() ) != NO_ERROR )
{
goto ErrorExit;
}
fImpersonate = TRUE;
if ( EnumType == NwsHandleListNdsSubTrees_Disk ||
EnumType == NwsHandleListNdsSubTrees_Print ||
EnumType == NwsHandleListNdsSubTrees_Any ||
EnumType == NwsHandleListContextInfo_Tree )
{
WCHAR lpServerName[NW_MAX_SERVER_LEN];
UNICODE_STRING ServerName;
UNICODE_STRING ObjectName;
ServerName.Length = 0;
ServerName.MaximumLength = sizeof( lpServerName );
ServerName.Buffer = lpServerName;
ObjectName.Buffer = NULL;
if ( EnumType == NwsHandleListContextInfo_Tree )
{
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
}
else
{
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
}
ObjectName.MaximumLength = ( wcslen( StrippedContainerName ) + 1 ) *
sizeof( WCHAR );
ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
StrippedContainerName,
PARSE_NDS_GET_TREE_NAME );
if ( ObjectName.Length == 0 || ObjectName.Buffer == NULL )
{
status = ERROR_PATH_NOT_FOUND;
goto ErrorExit;
}
//
// Open a NDS tree connection handle to \\treename
//
ntstatus = NwNdsOpenTreeHandle( &ObjectName,
&ContextHandle->TreeConnectionHandle );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
//
// Get the path to the container to open.
//
ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
StrippedContainerName,
PARSE_NDS_GET_PATH_NAME
);
if ( ObjectName.Length == 0 )
{
UNICODE_STRING Root;
RtlInitUnicodeString(&Root, L"[Root]");
//
// Resolve the path to get a NDS object id of [Root].
//
ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle,
&Root,
&ContextHandle->dwOid,
&ServerName,
NULL,
0 );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
wcscpy(ContextHandle->ContainerName, StrippedContainerName);
}
else
{
//
// Resolve the path to get a NDS object id.
//
ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle,
&ObjectName,
&ContextHandle->dwOid,
&ServerName,
NULL,
0 );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
wcscpy(ContextHandle->ContainerName, StrippedContainerName);
}
if ( ServerName.Length )
{
DWORD dwHandleType;
//
// NwNdsResolveName succeeded, but we were referred to
// another server, though ContextHandle->dwOid is still valid.
if ( ContextHandle->TreeConnectionHandle )
CloseHandle( ContextHandle->TreeConnectionHandle );
ContextHandle->TreeConnectionHandle = 0;
//
// Open a NDS generic connection handle to \\ServerName
//
ntstatus = NwNdsOpenGenericHandle( &ServerName,
&dwHandleType,
&ContextHandle->TreeConnectionHandle );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
ASSERT( dwHandleType == HANDLE_TYPE_NCP_SERVER );
}
//
// Check to see if object is either a Server, Directory Map, or Volume.
// If so, the object is a known leaf in terms of NDS, and therefore cannot
// be enumerated through NwNdsList API calls. We fail the OpenEnum call in these
// cases and pass back the type of object the leaf node was. This way the code in
// NWPROVAU!NPOpenEnum can call NwrOpenEnumServer, NwrOpenEnumVolume, or
// NwrOpenEnumDirectories accordingly.
//
{
BYTE RawResponse[TWO_KB];
DWORD RawResponseSize = sizeof(RawResponse);
DWORD dwStrLen;
PBYTE pbRawGetInfo;
ntstatus = NwNdsReadObjectInfo( ContextHandle->TreeConnectionHandle,
ContextHandle->dwOid,
RawResponse,
RawResponseSize );
if ( ntstatus != NO_ERROR )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
(void) NwRevertToSelf() ;
fImpersonate = FALSE;
pbRawGetInfo = RawResponse;
//
// The structure of a NDS_RESPONSE_GET_OBJECT_INFO consists of 4 DWORDs
// followed by two standard NDS format UNICODE strings. Below we jump pbRawGetInfo
// into the buffer, past the 4 DWORDs.
//
pbRawGetInfo += sizeof ( NDS_RESPONSE_GET_OBJECT_INFO );
//
// Now we get the length of the first string (Base Class).
//
dwStrLen = * ( DWORD * ) pbRawGetInfo;
//
// Now we point pbRawGetInfo to the first WCHAR of the first string (Base Class).
//
pbRawGetInfo += sizeof( DWORD );
//
// If the object is either a NCP Server, Volume, or a Directory Map, we fail
// the OpenEnum call and return the class type of the NDS leaf object. We do
// this because we cannot enumerate through NwNdsList() calls any subordinates,
// all browsing below these types are done through system redirector calls. So
// the client side of the provider will instead call NwOpenEnumVolumes or
// NwOpenEnumDirectories, respectively.
//
if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"NCP Server" ) )
{
if ( ClassTypeOfNDSLeaf )
*ClassTypeOfNDSLeaf = CLASS_TYPE_NCP_SERVER;
status = ERROR_NETWORK_ACCESS_DENIED;
goto ErrorExit;
}
if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Volume" ) )
{
if ( ClassTypeOfNDSLeaf )
*ClassTypeOfNDSLeaf = CLASS_TYPE_VOLUME;
status = ERROR_NETWORK_ACCESS_DENIED;
goto ErrorExit;
}
if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Directory Map" ) )
{
if ( ClassTypeOfNDSLeaf )
*ClassTypeOfNDSLeaf = CLASS_TYPE_DIRECTORY_MAP;
status = ERROR_NETWORK_ACCESS_DENIED;
goto ErrorExit;
}
} // End of block
}
else // EnumType is something other than a NDS Sub-tree
{
UNICODE_STRING TreeConnectStr;
TreeConnectStr.Buffer = NULL;
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
wcscpy(ContextHandle->ContainerName, StrippedContainerName);
wcscat(ContextHandle->ContainerName, L"\\");
//
// Open a tree connection handle to \Device\NwRdr\ContainerName
//
status = NwCreateTreeConnectName( StrippedContainerName,
NULL,
&TreeConnectStr );
if ( status != NO_ERROR )
{
goto ErrorExit;
}
status = NwOpenCreateConnection( &TreeConnectStr,
UserName,
Password,
StrippedContainerName,
FILE_LIST_DIRECTORY | SYNCHRONIZE |
( ValidateUserFlag? FILE_WRITE_DATA : 0 ),
CreateDisposition,
CreateOptions,
RESOURCETYPE_DISK, // When connecting beyond servername
&ContextHandle->TreeConnectionHandle,
NULL );
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
}
if (status == NO_ERROR)
{
VERSION_INFO vInfo;
if ( EnumType == NwsHandleListVolumes ||
EnumType == NwsHandleListVolumesQueues )
{
NWC_SERVER_INFO ServerInfo;
ServerInfo.hConn = ContextHandle->TreeConnectionHandle;
ServerInfo.ServerString.Length = 0;
ServerInfo.ServerString.MaximumLength = 0;
ServerInfo.ServerString.Buffer = NULL;
status = NWGetFileServerVersionInfo( (HANDLE) &ServerInfo,
&vInfo );
if ( status )
{
ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER;
status = NO_ERROR;
}
else
{
ContextHandle->dwMaxVolumes = (DWORD) vInfo.maxVolumes;
if ( ContextHandle->dwMaxVolumes == 0 )
{
ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER;
}
}
}
(void) NwRevertToSelf() ;
fImpersonate = FALSE;
if ( StrippedContainerName )
{
(void) LocalFree((HLOCAL) StrippedContainerName);
StrippedContainerName = NULL;
}
//
// Return the newly created context.
//
*EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
return status;
}
ErrorExit:
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( StrippedContainerName )
{
(void) LocalFree((HLOCAL) StrippedContainerName);
}
if ( ContextHandle )
{
if ( ContextHandle->TreeConnectionHandle )
CloseHandle( ContextHandle->TreeConnectionHandle );
ContextHandle->Signature = 0x0BADBAD0;
(void) LocalFree((HLOCAL) ContextHandle);
}
*EnumHandle = NULL;
if (status == ERROR_NOT_CONNECTED)
{
//
// Object name not found. We should return path not found.
//
status = ERROR_PATH_NOT_FOUND;
}
return status;
}
DWORD
NwrEnum(
IN NWWKSTA_CONTEXT_HANDLE EnumHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function
Arguments:
EnumHandle - Supplies a pointer to the context handle which identifies
what type of object we are enumerating and the string of the
container name to concatenate to the returned object.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
WN_BAD_HANDLE - The specified enumeration handle is invalid.
--*/ // NwrEnum
{
DWORD status;
LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle;
BOOL fImpersonate = FALSE ;
if (ContextHandle->Signature != NW_HANDLE_SIGNATURE) {
return WN_BAD_HANDLE;
}
//
// Impersonate the client
//
if ((status = NwImpersonateClient()) != NO_ERROR)
{
goto CleanExit;
}
fImpersonate = TRUE ;
*EntriesRead = 0;
*BytesNeeded = 0;
RtlZeroMemory(Buffer, BufferSize);
switch (ContextHandle->HandleType) {
case NwsHandleListConnections:
if (!(ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC))
{
status = NwEnumerateConnections(
&ContextHandle->ResumeId,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead,
ContextHandle->ConnectionType,
NULL
);
}
break;
case NwsHandleListContextInfo_Tree:
case NwsHandleListContextInfo_Server:
status = NwEnumContextInfo(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListServersAndNdsTrees:
status = NwEnumServersAndNdsTrees(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListVolumes:
status = NwEnumVolumes(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListNdsSubTrees_Disk:
status = NwEnumNdsSubTrees_Disk(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListNdsSubTrees_Print:
status = NwEnumNdsSubTrees_Print(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListNdsSubTrees_Any:
status = NwEnumNdsSubTrees_Any(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListQueues:
status = NwEnumQueues(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListVolumesQueues:
status = NwEnumVolumesQueues(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListDirectories:
status = NwEnumDirectories(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListPrintServers:
status = NwEnumPrintServers(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
case NwsHandleListPrintQueues:
status = NwEnumPrintQueues(
ContextHandle,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead
);
break;
default:
KdPrint(("NWWORKSTATION: NwrEnum unexpected handle type %lu\n",
ContextHandle->HandleType));
ASSERT(FALSE);
status = WN_BAD_HANDLE;
goto CleanExit ;
}
if (*EntriesRead > 0) {
switch ( ContextHandle->HandleType ) {
case NwsHandleListConnections:
case NwsHandleListContextInfo_Tree:
case NwsHandleListContextInfo_Server:
case NwsHandleListServersAndNdsTrees:
case NwsHandleListVolumes:
case NwsHandleListQueues:
case NwsHandleListVolumesQueues:
case NwsHandleListDirectories:
case NwsHandleListNdsSubTrees_Disk:
case NwsHandleListNdsSubTrees_Any:
{
DWORD i;
LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer;
//
// Replace pointers to strings with offsets as need
//
if ((ContextHandle->HandleType == NwsHandleListConnections)
&& (ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC))
{
//
// NwrEnumGWDevices already return offsets.
//
break ;
}
for (i = 0; i < *EntriesRead; i++, NetR++) {
if (NetR->lpLocalName != NULL) {
NetR->lpLocalName = (LPWSTR)
((DWORD_PTR) (NetR->lpLocalName) - (DWORD_PTR) Buffer);
}
NetR->lpRemoteName =
(LPWSTR) ((DWORD_PTR) (NetR->lpRemoteName) - (DWORD_PTR)Buffer);
if (NetR->lpComment != NULL) {
NetR->lpComment = (LPWSTR) ((DWORD_PTR) (NetR->lpComment) -
(DWORD_PTR) Buffer);
}
if (NetR->lpProvider != NULL) {
NetR->lpProvider =
(LPWSTR) ((DWORD_PTR) (NetR->lpProvider) -
(DWORD_PTR) Buffer);
}
}
break;
}
case NwsHandleListPrintServers:
case NwsHandleListPrintQueues:
case NwsHandleListNdsSubTrees_Print:
{
DWORD i;
PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) Buffer;
//
// Sort the entries in the buffer
//
if ( *EntriesRead > 1 )
qsort( Buffer, *EntriesRead,
sizeof( PRINTER_INFO_1W ), SortFunc );
//
// Replace pointers to strings with offsets
//
for (i = 0; i < *EntriesRead; i++, pPrinterInfo1++) {
MarshallDownStructure( (LPBYTE) pPrinterInfo1,
PrinterInfo1Offsets,
Buffer );
}
break;
}
default:
KdPrint(("NWWORKSTATION: NwrEnum (pointer to offset code) unexpected handle type %lu\n", ContextHandle->HandleType));
ASSERT( FALSE );
break;
}
}
CleanExit:
if (fImpersonate)
(void) NwRevertToSelf() ;
return status;
}
DWORD
NwrEnumConnections(
IN NWWKSTA_CONTEXT_HANDLE EnumHandle,
IN DWORD EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead,
IN DWORD fImplicitConnections
)
/*++
Routine Description:
This function is an alternate to NwrEnum. It only accepts handles
that are opened with ListConnections. This function takes a flag
indicating whether we need to show all implicit connections or not.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff ERROR_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
fImplicitConnections - TRUE if we also want to get implicit connections,
FALSE otherwise.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
ERROR_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwrEnumConnections
{
DWORD status;
LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle;
if ( (ContextHandle->Signature != NW_HANDLE_SIGNATURE)
|| ( ContextHandle->HandleType != NwsHandleListConnections )
)
{
return WN_BAD_HANDLE;
}
*EntriesRead = 0;
*BytesNeeded = 0;
RtlZeroMemory(Buffer, BufferSize);
if ( fImplicitConnections )
ContextHandle->ConnectionType |= CONNTYPE_IMPLICIT;
if ((status = NwImpersonateClient()) != NO_ERROR)
goto ErrorExit;
status = NwEnumerateConnections(
&ContextHandle->ResumeId,
EntriesRequested,
Buffer,
BufferSize,
BytesNeeded,
EntriesRead,
ContextHandle->ConnectionType,
NULL
);
if (*EntriesRead > 0) {
//
// Replace pointers to strings with offsets
//
DWORD i;
LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer;
for (i = 0; i < *EntriesRead; i++, NetR++) {
if (NetR->lpLocalName != NULL) {
NetR->lpLocalName = (LPWSTR)
((DWORD_PTR) (NetR->lpLocalName) - (DWORD_PTR) Buffer);
}
NetR->lpRemoteName =
(LPWSTR) ((DWORD_PTR) (NetR->lpRemoteName) - (DWORD_PTR)Buffer);
if (NetR->lpComment != NULL) {
NetR->lpComment = (LPWSTR) ((DWORD_PTR) (NetR->lpComment) -
(DWORD_PTR) Buffer);
}
if (NetR->lpProvider != NULL) {
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) (NetR->lpProvider) -
(DWORD_PTR) Buffer);
}
}
}
(void) NwRevertToSelf();
ErrorExit:
return status;
}
DWORD
NwEnumContextInfo(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all of the bindery servers that are currently
connected, then sets the context handle so that the next NPEnumResource
call goes to the NDS subtree for the user's NDS context information
(if using NDS).
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumContextInfo
{
DWORD status = NO_ERROR;
DWORD_PTR tempResumeId = 0;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
DWORD LastObjectId = (DWORD) ContextHandle->ResumeId;
while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR )
{
tempResumeId = ContextHandle->ResumeId;
status = NwGetNextServerConnection( ContextHandle );
if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
{
//
// Pack bindery server name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
L"\\\\",
NULL,
(LPWSTR) ContextHandle->ResumeId, // A server name
RESOURCE_CONTEXT,
RESOURCEDISPLAYTYPE_SERVER,
RESOURCEUSAGE_CONTAINER,
RESOURCETYPE_ANY,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer,
// backup ResumeId to previous entry.
//
ContextHandle->ResumeId = tempResumeId;
ContextHandle->NdsRawDataCount += 1;
if (*EntriesRead)
{
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else
{
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR)
{
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
}
}
else if ( status == WN_NO_MORE_ENTRIES )
{
//
// We processed the last item in list, so
// start enumerating servers.
//
ContextHandle->ResumeId = 0;
LastObjectId = 0;
if ( ContextHandle->HandleType == NwsHandleListContextInfo_Tree )
{
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
}
}
}
if ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS )
{
ContextHandle->HandleType = NwsHandleListNdsSubTrees_Any;
status = NO_ERROR;
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
{
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumServersAndNdsTrees(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the servers and NDS trees on the local
network by: 1) scanning the bindery for file server objects on the
preferred server and 2) scanning the bindery for directory servers
(NDS trees) on the preferred server. The server and tree entries are
returned in an array of NETRESOURCE entries; each servername is
prefixed by \\.
The ContextHandle->ResumeId field is initially -1 before
enumeration begins and contains the object ID of the last server
or NDS tree object returned, depending on the value of
ContextHandle->dwUsingNds.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumServersAndNdsTrees
{
DWORD status = NO_ERROR;
DWORD_PTR tempResumeId = 0;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
SERVERNAME ServerName; // OEM server name
LPWSTR UServerName = NULL; // Unicode server name
DWORD LastObjectId = (DWORD) ContextHandle->ResumeId;
while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS &&
FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR )
{
tempResumeId = ContextHandle->ResumeId;
//
// Call the scan bindery object NCP to scan for all NDS
// tree objects.
//
status = NwGetNextNdsTreeEntry( ContextHandle );
if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
{
//
// Pack tree name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
L"\\\\",
NULL,
(LPWSTR) ContextHandle->ResumeId, // This is a NDS tree name
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_TREE,
RESOURCEUSAGE_CONTAINER,
RESOURCETYPE_ANY,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer, backup ResumeId to
// previous entry.
//
ContextHandle->ResumeId = tempResumeId;
ContextHandle->NdsRawDataCount += 1;
if (*EntriesRead)
{
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else
{
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR)
{
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
}
}
else if ( status == WN_NO_MORE_ENTRIES )
{
//
// We processed the last item in list, so
// start enumerating servers.
//
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
ContextHandle->ResumeId = (DWORD_PTR) -1;
LastObjectId = (DWORD) -1;
}
}
if ( status == WN_NO_MORE_ENTRIES)
{
status = NO_ERROR;
}
while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR )
{
RtlZeroMemory(ServerName, sizeof(ServerName));
//
// Call the scan bindery object NCP to scan for all file
// server objects.
//
status = NwGetNextServerEntry(
ContextHandle->TreeConnectionHandle,
&LastObjectId,
ServerName
);
if (status == NO_ERROR && NwConvertToUnicode(&UServerName, ServerName))
{
//
// Pack server name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
L"\\\\",
NULL,
UServerName,
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_SERVER,
RESOURCEUSAGE_CONTAINER,
RESOURCETYPE_ANY,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer.
//
if (*EntriesRead)
{
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else
{
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR)
{
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
ContextHandle->ResumeId = (DWORD_PTR) LastObjectId;
}
(void) LocalFree((HLOCAL) UServerName);
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
{
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumVolumes(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the volumes on a server by
iteratively getting the volume name for each volume number from
0 - 31 until we run into the first volume number that does not
map to a volume name (this method assumes that volume numbers
are used contiguously in ascending order). The volume entries
are returned in an array of NETRESOURCE entries; each volume
name if prefixed by \\Server\.
The ContextHandle->ResumeId field always indicates the next
volume entry to return. It is initially set to 0, which indicates
the first volume number to get.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumVolumes
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name
LPWSTR UVolumeName = NULL; // Unicode volume name
DWORD NextVolumeNumber = (DWORD) ContextHandle->ResumeId;
DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes;
ULONG Failures = 0;
if (NextVolumeNumber == MaxVolumeNumber) {
//
// Reached the end of enumeration
//
return WN_NO_MORE_ENTRIES;
}
while (FitInBuffer &&
EntriesRequested > *EntriesRead &&
NextVolumeNumber < MaxVolumeNumber &&
status == NO_ERROR) {
RtlZeroMemory(VolumeName, sizeof(VolumeName));
//
// Call the scan bindery object NCP to scan for all file
// volume objects.
//
status = NwGetNextVolumeEntry(
ContextHandle->TreeConnectionHandle,
NextVolumeNumber++,
VolumeName
);
if (status == NO_ERROR) {
if (VolumeName[0] == 0) {
//
// Got an empty volume name back for the next volume number
// which indicates there is no volume associated with the
// volume number but still got error success.
//
// Treat this as having reached the end of the enumeration
// only if we've gotten two three empty volumes in a row
// or reached the max number of volumes because there are
// some cases where there are holes in the way that volumes
// are allocated.
//
Failures++;
if ( Failures <= 3 ) {
continue;
} else {
NextVolumeNumber = MaxVolumeNumber;
ContextHandle->ResumeId = MaxVolumeNumber;
if (*EntriesRead == 0) {
status = WN_NO_MORE_ENTRIES;
}
}
} else if (NwConvertToUnicode(&UVolumeName, VolumeName)) {
//
// Pack volume name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
ContextHandle->ContainerName,
NULL,
UVolumeName,
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_SHARE,
#ifdef NT1057
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER,
#else
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE,
#endif
RESOURCETYPE_DISK,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA) {
//
// Could not write current entry into output buffer.
//
if (*EntriesRead) {
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else {
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR) {
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
ContextHandle->ResumeId = NextVolumeNumber;
}
(void) LocalFree((HLOCAL) UVolumeName);
}
//
// We got an entry, so reset the failure counter.
//
Failures = 0;
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumNdsSubTrees_Disk(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates the sub-trees of a given NDS tree
handle. It returns the fully-qualified UNC path of the sub-tree
entries in an array of NETRESOURCE entries.
The ContextHandle->ResumeId field is 0 initially, and contains
a pointer to the subtree name string of the last sub-tree
returned. If there are no more sub-trees to return, this
field is set to -1.
Arguments:
ContextHandle - Supplies the enum context handle. It contains
an opened NDS tree handle.
EntriesRequested - Supplies the number of entries to return. If
this value is -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumNdsSubTrees_Disk
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize = 0;
LPWSTR SubTreeName = NULL;
DWORD ResourceScope = 0;
DWORD ResourceType = 0;
DWORD ResourceDisplayType = 0;
DWORD ResourceUsage = 0;
LPWSTR StrippedObjectName = NULL;
if (ContextHandle->ResumeId == (DWORD_PTR) -1)
{
//
// Reached the end of enumeration.
//
return WN_NO_MORE_ENTRIES;
}
while (FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR)
{
if ( ContextHandle->ResumeId == 0 )
{
//
// Get the first subtree entry.
//
status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
}
//
// Either ResumeId contains the first entry we just got from
// NwGetFirstDirectoryEntry or it contains the next directory
// entry to return.
//
if (status == NO_ERROR && ContextHandle->ResumeId != 0)
{
BYTE ClassType;
LPWSTR newPathStr = NULL;
LPWSTR tempStr = NULL;
WORD tempStrLen;
//
// Get current subtree data from ContextHandle
//
ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
&SubTreeName,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage,
&StrippedObjectName );
if ( StrippedObjectName == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
switch( ClassType )
{
case CLASS_TYPE_COUNTRY:
case CLASS_TYPE_DIRECTORY_MAP:
case CLASS_TYPE_NCP_SERVER:
case CLASS_TYPE_ORGANIZATION:
case CLASS_TYPE_ORGANIZATIONAL_UNIT:
case CLASS_TYPE_VOLUME:
//
// Need to build a string with the new NDS UNC path for subtree object
//
newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( wcslen( StrippedObjectName ) +
wcslen( ContextHandle->ContainerName ) +
3 ) * sizeof(WCHAR) );
if ( newPathStr == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_TREE_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcscpy( newPathStr, L"\\\\" );
wcsncat( newPathStr, tempStr, tempStrLen );
wcscat( newPathStr, L"\\" );
wcscat( newPathStr, StrippedObjectName );
}
(void) LocalFree((HLOCAL) StrippedObjectName );
StrippedObjectName = NULL;
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_PATH_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcscat( newPathStr, L"." );
wcsncat( newPathStr, tempStr, tempStrLen );
}
//
// Pack subtree name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
NULL,
newPathStr,
ResourceScope,
ResourceDisplayType,
ResourceUsage,
ResourceType,
NULL,
NULL,
&EntrySize );
if ( status == NO_ERROR )
{
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
}
if ( newPathStr )
(void) LocalFree( (HLOCAL) newPathStr );
break;
case CLASS_TYPE_ALIAS:
case CLASS_TYPE_AFP_SERVER:
case CLASS_TYPE_BINDERY_OBJECT:
case CLASS_TYPE_BINDERY_QUEUE:
case CLASS_TYPE_COMPUTER:
case CLASS_TYPE_GROUP:
case CLASS_TYPE_LOCALITY:
case CLASS_TYPE_ORGANIZATIONAL_ROLE:
case CLASS_TYPE_PRINTER:
case CLASS_TYPE_PRINT_SERVER:
case CLASS_TYPE_PROFILE:
case CLASS_TYPE_QUEUE:
case CLASS_TYPE_TOP:
case CLASS_TYPE_UNKNOWN:
case CLASS_TYPE_USER:
break;
default:
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk - Unhandled switch statement case %lu\n", ClassType ));
ASSERT( FALSE );
break;
}
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer.
//
if (*EntriesRead)
{
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else
{
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR)
{
//
// Get next directory entry.
//
status = NwGetNextNdsSubTreeEntry( ContextHandle );
}
}
if (status == WN_NO_MORE_ENTRIES)
{
ContextHandle->ResumeId = (DWORD_PTR) -1;
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
#if DBG
IF_DEBUG(ENUM)
{
KdPrint(("NwEnumNdsSubTrees_Disk returns %lu\n", status));
}
#endif
return status;
}
DWORD
NwEnumNdsSubTrees_Print(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the NDS subtree objects that are either containers,
queues, printers, or servers from a given NDS tree or subtree. The entries are
returned in an array of PRINTER_INFO_1 entries and each name is prefixed
by the parent path in NDS UNC style (ex. \\tree\CN=foo.OU=bar.O=blah).
The ContextHandle->ResumeId field is initially (DWORD_PTR) -1 before
enumeration begins and contains the object ID of the last NDS object returned.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes copied or required to get all
the requested entries.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
Return Value:
NO_ERROR - Buffer contains all the entries requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit the requested entries.
--*/ // NwEnumNdsSubTrees_Print
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
DWORD EntrySize;
BOOL FitInBuffer = TRUE;
LPWSTR SubTreeName = NULL;
DWORD ResourceScope = 0;
DWORD ResourceType = 0;
DWORD ResourceDisplayType = 0;
DWORD ResourceUsage = 0;
LPWSTR StrippedObjectName = NULL;
BYTE ClassType = 0;
LPWSTR newPathStr = NULL;
LPWSTR tempStr = NULL;
WORD tempStrLen = 0;
while ( EntriesRequested > *EntriesRead &&
( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER)))
{
if (ContextHandle->ResumeId == 0)
{
//
// Get the first subtree entry.
//
status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
}
//
// Either ResumeId contains the first entry we just got from
// NwGetFirstDirectoryEntry or it contains the next directory
// entry to return.
//
if (status == NO_ERROR && ContextHandle->ResumeId != 0)
{
//
// Get current subtree data from ContextHandle
//
ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
&SubTreeName,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage,
&StrippedObjectName );
if ( StrippedObjectName == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
switch( ClassType )
{
case CLASS_TYPE_COUNTRY:
case CLASS_TYPE_ORGANIZATION:
case CLASS_TYPE_ORGANIZATIONAL_UNIT:
case CLASS_TYPE_NCP_SERVER:
case CLASS_TYPE_QUEUE:
//
// Need to build a string with the new NDS UNC path for subtree object
//
newPathStr = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
( wcslen( StrippedObjectName ) +
wcslen( ContextHandle->ContainerName ) +
2 ) * sizeof(WCHAR) );
if ( newPathStr == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_TREE_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcsncpy( newPathStr, tempStr, tempStrLen );
wcscat( newPathStr, L"\\" );
wcscat( newPathStr, StrippedObjectName );
}
(void) LocalFree((HLOCAL) StrippedObjectName );
StrippedObjectName = NULL;
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_PATH_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcscat( newPathStr, L"." );
wcsncat( newPathStr, tempStr, tempStrLen );
}
switch( ClassType )
{
case CLASS_TYPE_COUNTRY:
case CLASS_TYPE_ORGANIZATION:
case CLASS_TYPE_ORGANIZATIONAL_UNIT:
//
// Pack sub-tree container name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
newPathStr,
PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1,
&EntrySize );
break;
case CLASS_TYPE_NCP_SERVER:
//
// Pack server name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
newPathStr,
PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3,
&EntrySize );
break;
case CLASS_TYPE_QUEUE:
//
// Pack print server queue name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
L"\\\\",
newPathStr,
PRINTER_ENUM_ICON8,
&EntrySize );
break;
default:
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType ));
ASSERT(FALSE);
break;
}
switch ( status )
{
case ERROR_INSUFFICIENT_BUFFER:
FitInBuffer = FALSE;
// Falls through
case NO_ERROR:
*BytesNeeded += EntrySize;
(*EntriesRead)++;
break;
default:
break;
}
if ( newPathStr )
(void) LocalFree( (HLOCAL) newPathStr );
break;
case CLASS_TYPE_ALIAS:
case CLASS_TYPE_AFP_SERVER:
case CLASS_TYPE_BINDERY_OBJECT:
case CLASS_TYPE_BINDERY_QUEUE:
case CLASS_TYPE_COMPUTER:
case CLASS_TYPE_DIRECTORY_MAP:
case CLASS_TYPE_GROUP:
case CLASS_TYPE_LOCALITY:
case CLASS_TYPE_ORGANIZATIONAL_ROLE:
case CLASS_TYPE_PRINTER:
case CLASS_TYPE_PRINT_SERVER:
case CLASS_TYPE_PROFILE:
case CLASS_TYPE_TOP:
case CLASS_TYPE_UNKNOWN:
case CLASS_TYPE_USER:
case CLASS_TYPE_VOLUME:
break;
default:
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType ));
ASSERT( FALSE );
break;
}
if ( status == NO_ERROR || status == ERROR_INSUFFICIENT_BUFFER )
{
//
// Get next directory entry.
//
status = NwGetNextNdsSubTreeEntry( ContextHandle );
}
}
}
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if ( !FitInBuffer )
{
*EntriesRead = 0;
status = ERROR_INSUFFICIENT_BUFFER;
}
else if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
{
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumNdsSubTrees_Any(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates the sub-trees of a given NDS tree
handle. It returns the fully-qualified UNC path of ANY sub-tree
entries in an array of NETRESOURCE entries.
The ContextHandle->ResumeId field is 0 initially, and contains
a pointer to the subtree name string of the last sub-tree
returned. If there are no more sub-trees to return, this
field is set to (DWORD_PTR) -1.
Arguments:
ContextHandle - Supplies the enum context handle. It contains
an opened NDS tree handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumNdsSubTrees_Any
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize = 0;
LPWSTR SubTreeName = NULL;
DWORD ResourceScope = 0;
DWORD ResourceType = 0;
DWORD ResourceDisplayType = 0;
DWORD ResourceUsage = 0;
LPWSTR StrippedObjectName = NULL;
if (ContextHandle->ResumeId == (DWORD_PTR) -1)
{
//
// Reached the end of enumeration.
//
return WN_NO_MORE_ENTRIES;
}
while (FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR)
{
if ( ContextHandle->ResumeId == 0 )
{
//
// Get the first subtree entry.
//
status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
}
//
// Either ResumeId contains the first entry we just got from
// NwGetFirstDirectoryEntry or it contains the next directory
// entry to return.
//
if (status == NO_ERROR && ContextHandle->ResumeId != 0)
{
BYTE ClassType;
LPWSTR newPathStr = NULL;
LPWSTR tempStr = NULL;
WORD tempStrLen;
//
// Get current subtree data from ContextHandle
//
ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
&SubTreeName,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage,
&StrippedObjectName );
if ( StrippedObjectName == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
switch( ClassType )
{
case CLASS_TYPE_COUNTRY:
case CLASS_TYPE_ORGANIZATION:
case CLASS_TYPE_ORGANIZATIONAL_UNIT:
case CLASS_TYPE_VOLUME:
case CLASS_TYPE_DIRECTORY_MAP:
case CLASS_TYPE_NCP_SERVER:
case CLASS_TYPE_QUEUE:
//
// Need to build a string with the new NDS UNC path for subtree object
//
newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( wcslen( StrippedObjectName ) +
wcslen( ContextHandle->ContainerName ) +
3 ) * sizeof(WCHAR) );
if ( newPathStr == NULL )
{
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_TREE_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcscpy( newPathStr, L"\\\\" );
wcsncat( newPathStr, tempStr, tempStrLen );
wcscat( newPathStr, L"\\" );
wcscat( newPathStr, StrippedObjectName );
}
(void) LocalFree((HLOCAL) StrippedObjectName );
StrippedObjectName = NULL;
tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
ContextHandle->ContainerName,
PARSE_NDS_GET_PATH_NAME );
tempStrLen /= sizeof( WCHAR );
if ( tempStrLen > 0 )
{
wcscat( newPathStr, L"." );
wcsncat( newPathStr, tempStr, tempStrLen );
}
//
// Pack subtree name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
NULL,
newPathStr,
ResourceScope,
ResourceDisplayType,
ResourceUsage,
ResourceType,
NULL,
NULL,
&EntrySize );
if ( status == NO_ERROR )
{
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
}
if ( newPathStr )
(void) LocalFree( (HLOCAL) newPathStr );
break;
case CLASS_TYPE_ALIAS:
case CLASS_TYPE_AFP_SERVER:
case CLASS_TYPE_BINDERY_OBJECT:
case CLASS_TYPE_BINDERY_QUEUE:
case CLASS_TYPE_COMPUTER:
case CLASS_TYPE_GROUP:
case CLASS_TYPE_LOCALITY:
case CLASS_TYPE_ORGANIZATIONAL_ROLE:
case CLASS_TYPE_PRINTER:
case CLASS_TYPE_PRINT_SERVER:
case CLASS_TYPE_PROFILE:
case CLASS_TYPE_TOP:
case CLASS_TYPE_UNKNOWN:
case CLASS_TYPE_USER:
break;
default:
KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any - Unhandled switch statement case %lu\n", ClassType ));
ASSERT( FALSE );
break;
}
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer.
//
if (*EntriesRead)
{
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else
{
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR)
{
//
// Get next directory entry.
//
status = NwGetNextNdsSubTreeEntry( ContextHandle );
}
}
if (status == WN_NO_MORE_ENTRIES)
{
ContextHandle->ResumeId = (DWORD_PTR) -1;
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
#if DBG
IF_DEBUG(ENUM)
{
KdPrint(("NwEnumNdsSubTrees_Any returns %lu\n", status));
}
#endif
return status;
}
DWORD
NwEnumVolumesQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the volumes and queues on a server.
The queue entries are returned in an array of NETRESOURCE entries;
each queue name is prefixed by \\Server\.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned if WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumVolumesQueues
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name
LPWSTR UVolumeName = NULL; // Unicode volume name
DWORD NextObject = (DWORD) ContextHandle->ResumeId;
DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes;
ULONG Failures = 0;
//
// tommye - bug 139466
//
// removed if (NextObject >= 0) becaue NextObject is a DWORD
//
while (FitInBuffer &&
EntriesRequested > *EntriesRead &&
ContextHandle->ConnectionType == CONNTYPE_DISK &&
(NextObject < MaxVolumeNumber) &&
status == NO_ERROR) {
RtlZeroMemory(VolumeName, sizeof(VolumeName));
//
// Call the scan bindery object NCP to scan for all file
// volume objects.
//
status = NwGetNextVolumeEntry(
ContextHandle->TreeConnectionHandle,
NextObject++,
VolumeName
);
if (status == NO_ERROR) {
if (VolumeName[0] == 0) {
//
// Got an empty volume name back for the next volume number
// which indicates there is no volume associated with the
// volume number but still got error success.
//
// Treat this as having reached the end of the enumeration
// if we have had three failures in a row. This will allow
// us to function when there are small holes in the drive
// list.
//
Failures++;
if ( Failures <= 3 ) {
continue;
} else {
NextObject = (DWORD) -1;
ContextHandle->ResumeId = (DWORD_PTR) -1;
ContextHandle->ConnectionType = CONNTYPE_PRINT;
}
} else if (NwConvertToUnicode(&UVolumeName, VolumeName)) {
//
// Pack volume name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
ContextHandle->ContainerName,
NULL,
UVolumeName,
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_SHARE,
#ifdef NT1057
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER,
#else
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE,
#endif
RESOURCETYPE_DISK,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA) {
//
// Could not write current entry into output buffer.
//
if (*EntriesRead) {
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else {
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR) {
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
ContextHandle->ResumeId = NextObject;
}
(void) LocalFree((HLOCAL) UVolumeName);
}
//
// Reset the failures counter.
//
Failures = 0;
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
{
status = NO_ERROR;
}
if ( *EntriesRead == 0 &&
status == NO_ERROR &&
ContextHandle->ConnectionType == CONNTYPE_DISK )
{
ContextHandle->ConnectionType = CONNTYPE_PRINT;
ContextHandle->ResumeId = (DWORD_PTR) -1;
}
//
// The user needs to be validated on a netware311 server to
// get the print queues. So, we need to close the handle and
// open a new one with WRITE access. If any error occurred while
// we are enumerating the print queues, we will abort and
// assume there are no print queues on the server.
//
if ( FitInBuffer &&
EntriesRequested > *EntriesRead &&
ContextHandle->ConnectionType == CONNTYPE_PRINT &&
status == NO_ERROR )
{
UNICODE_STRING TreeConnectStr;
DWORD QueueEntriesRead = 0;
(void) NtClose(ContextHandle->TreeConnectionHandle);
//
// Open a tree connection handle to \Device\NwRdr\ContainerName
//
status = NwCreateTreeConnectName(
ContextHandle->ContainerName,
NULL,
&TreeConnectStr );
if (status != NO_ERROR)
return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES );
status = NwOpenCreateConnection(
&TreeConnectStr,
NULL,
NULL,
ContextHandle->ContainerName,
FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_DATA,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
RESOURCETYPE_PRINT, // Only matters when connecting beyond servername
&ContextHandle->TreeConnectionHandle,
NULL );
(void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
if (status != NO_ERROR)
return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES );
status = NwEnumQueues(
ContextHandle,
EntriesRequested == (DWORD_PTR) -1?
EntriesRequested : (EntriesRequested - *EntriesRead),
FixedPortion,
(DWORD) ((LPBYTE) EndOfVariableData - (LPBYTE) FixedPortion),
BytesNeeded,
&QueueEntriesRead );
if ( status == NO_ERROR )
{
*EntriesRead += QueueEntriesRead;
}
else if ( *EntriesRead )
{
//
// As long as we read something into the buffer,
// we should return success.
//
status = NO_ERROR;
*BytesNeeded = 0;
}
}
if ( status == NO_ERROR &&
*EntriesRead == 0 &&
ContextHandle->ConnectionType == CONNTYPE_PRINT )
{
return WN_NO_MORE_ENTRIES;
}
return status;
}
DWORD
NwEnumQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the queues on a server.
The queue entries are returned in an array of NETRESOURCE entries;
each queue name is prefixed by \\Server\.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumQueues
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
DWORD NextObject = (DWORD) ContextHandle->ResumeId;
SERVERNAME QueueName; // OEM queue name
LPWSTR UQueueName = NULL; // Unicode queue name
while ( FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR ) {
RtlZeroMemory(QueueName, sizeof(QueueName));
//
// Call the scan bindery object NCP to scan for all file
// volume objects.
//
status = NwGetNextQueueEntry(
ContextHandle->TreeConnectionHandle,
&NextObject,
QueueName
);
if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) {
//
// Pack server name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
ContextHandle->ContainerName,
NULL,
UQueueName,
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_SHARE,
RESOURCEUSAGE_CONNECTABLE,
RESOURCETYPE_PRINT,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA) {
//
// Could not write current entry into output buffer.
//
if (*EntriesRead) {
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else {
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR) {
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
ContextHandle->ResumeId = (DWORD_PTR) NextObject;
}
(void) LocalFree((HLOCAL) UQueueName);
}
}
if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumDirectories(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates the directories of a given directory
handle by calling NtQueryDirectoryFile. It returns the
fully-qualified UNC path of the directory entries in an array
of NETRESOURCE entries.
The ContextHandle->ResumeId field is 0 initially, and contains
a pointer to the directory name string of the last directory
returned. If there are no more directories to return, this
field is set to (DWORD_PTR) -1.
Arguments:
ContextHandle - Supplies the enum context handle. It contains
an opened directory handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes required to get the
first entry. This value is returned iff WN_MORE_DATA is
the return code, and Buffer is too small to even fit one
entry.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
NO_ERROR is returned as long as at least one entry was written
into Buffer but does not necessarily mean that it's the number
of EntriesRequested.
Return Value:
NO_ERROR - At least one entry was written to output buffer,
irregardless of the number requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit a single entry.
--*/ // NwEnumDirectories
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
BOOL FitInBuffer = TRUE;
DWORD EntrySize;
if (ContextHandle->ResumeId == (DWORD_PTR) -1) {
//
// Reached the end of enumeration.
//
return WN_NO_MORE_ENTRIES;
}
while (FitInBuffer &&
EntriesRequested > *EntriesRead &&
status == NO_ERROR) {
if (ContextHandle->ResumeId == 0) {
//
// Get the first directory entry.
//
status = NwGetFirstDirectoryEntry(
ContextHandle->TreeConnectionHandle,
(LPWSTR *) &ContextHandle->ResumeId
);
}
//
// Either ResumeId contains the first entry we just got from
// NwGetFirstDirectoryEntry or it contains the next directory
// entry to return.
//
if (ContextHandle->ResumeId != 0) {
//
// Pack directory name into output buffer.
//
status = NwWriteNetResourceEntry(
&FixedPortion,
&EndOfVariableData,
ContextHandle->ContainerName,
NULL,
(LPWSTR) ContextHandle->ResumeId,
RESOURCE_GLOBALNET,
RESOURCEDISPLAYTYPE_SHARE,
#ifdef NT1057
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER,
#else
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE,
#endif
RESOURCETYPE_DISK,
NULL,
NULL,
&EntrySize
);
if (status == WN_MORE_DATA) {
//
// Could not write current entry into output buffer.
//
if (*EntriesRead) {
//
// Still return success because we got at least one.
//
status = NO_ERROR;
}
else {
*BytesNeeded = EntrySize;
}
FitInBuffer = FALSE;
}
else if (status == NO_ERROR) {
//
// Note that we've returned the current entry.
//
(*EntriesRead)++;
//
// Free memory allocated to save resume point, which is
// a buffer that contains the last directory we returned.
//
if (ContextHandle->ResumeId != 0) {
(void) LocalFree((HLOCAL) ContextHandle->ResumeId);
ContextHandle->ResumeId = 0;
}
//
// Get next directory entry.
//
status = NwGetNextDirectoryEntry(
(LPWSTR) ContextHandle->TreeConnectionHandle,
(LPWSTR *) &ContextHandle->ResumeId
);
}
}
if (status == WN_NO_MORE_ENTRIES) {
ContextHandle->ResumeId = (DWORD_PTR) -1;
}
}
//
// User asked for more than there are entries. We just say that
// all is well.
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("EnumDirectories returns %lu\n", status));
}
#endif
return status;
}
DWORD
NwEnumPrintServers(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the servers and NDS tree on the local network
by scanning the bindery for file server or directory objects on the
preferred server. The server and tree entries are returned in an
array of PRINTER_INFO_1 entries; each entry name is prefixed by
\\.
The ContextHandle->ResumeId field is initially (DWORD_PTR) -1 before
enumeration begins and contains the object ID of the last server
object returned.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes copied or required to get all
the requested entries.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
Return Value:
NO_ERROR - Buffer contains all the entries requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit the requested entries.
--*/ // NwEnumPrintServers
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
DWORD EntrySize;
BOOL FitInBuffer = TRUE;
SERVERNAME ServerName; // OEM server name
LPWSTR UServerName = NULL; // Unicode server name
DWORD LastObjectId = (DWORD) ContextHandle->ResumeId;
WCHAR TempBuffer[500];
while ( EntriesRequested > *EntriesRead &&
ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS &&
((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER)))
{
//
// Call the scan bindery object NCP to scan for all NDS
// tree objects.
//
status = NwGetNextNdsTreeEntry( ContextHandle );
if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
{
//
// Put tree name into a buffer
//
RtlZeroMemory( TempBuffer, 500 );
wcscat( TempBuffer, (LPWSTR) ContextHandle->ResumeId );
//
// Pack server name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
TempBuffer, // This is a NDS tree name
PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1,
&EntrySize
);
switch ( status )
{
case ERROR_INSUFFICIENT_BUFFER:
FitInBuffer = FALSE;
// Falls through
case NO_ERROR:
*BytesNeeded += EntrySize;
(*EntriesRead)++;
// ContextHandle->ResumeId = LastObjectId;
break;
default:
break;
}
}
else if ( status == WN_NO_MORE_ENTRIES )
{
//
// We processed the last item in list, so
// start enumerating servers.
//
ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
ContextHandle->ResumeId = (DWORD_PTR) -1;
LastObjectId = (DWORD) -1;
}
}
status = NO_ERROR;
while ( EntriesRequested > *EntriesRead &&
ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) {
RtlZeroMemory(ServerName, sizeof(ServerName));
//
// Call the scan bindery object NCP to scan for all file
// server objects.
//
status = NwGetNextServerEntry(
ContextHandle->TreeConnectionHandle,
&LastObjectId,
ServerName
);
if (status == NO_ERROR && NwConvertToUnicode(&UServerName,ServerName)) {
//
// Pack server name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
NULL,
UServerName,
PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3,
&EntrySize
);
switch ( status )
{
case ERROR_INSUFFICIENT_BUFFER:
FitInBuffer = FALSE;
// Falls through
case NO_ERROR:
*BytesNeeded += EntrySize;
(*EntriesRead)++;
ContextHandle->ResumeId = (DWORD_PTR) LastObjectId;
break;
default:
break;
}
(void) LocalFree((HLOCAL) UServerName);
}
}
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if ( !FitInBuffer ) {
*EntriesRead = 0;
status = ERROR_INSUFFICIENT_BUFFER;
}
else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
return status;
}
DWORD
NwEnumPrintQueues(
IN LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD_PTR EntriesRequested,
OUT LPBYTE Buffer,
IN DWORD BufferSize,
OUT LPDWORD BytesNeeded,
OUT LPDWORD EntriesRead
)
/*++
Routine Description:
This function enumerates all the print queues on a server by scanning
the bindery on the server for print queues objects.
The print queues entries are returned in an array of PRINTER_INFO_1 entries
and each printer name is prefixed by \\Server\.
The ContextHandle->ResumeId field is initially (DWORD_PTR) -1 before
enumeration begins and contains the object ID of the last print queue
object returned.
Arguments:
ContextHandle - Supplies the enum context handle.
EntriesRequested - Supplies the number of entries to return. If
this value is (DWORD_PTR) -1, return all available entries.
Buffer - Receives the entries we are listing.
BufferSize - Supplies the size of the output buffer.
BytesNeeded - Receives the number of bytes copied or required to get all
the requested entries.
EntriesRead - Receives the number of entries returned in Buffer.
This value is only returned iff NO_ERROR is the return code.
Return Value:
NO_ERROR - Buffer contains all the entries requested.
WN_NO_MORE_ENTRIES - No entries left to return.
WN_MORE_DATA - The buffer was too small to fit the requested entries.
--*/ // NwEnumPrintQueues
{
DWORD status = NO_ERROR;
LPBYTE FixedPortion = Buffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
DWORD EntrySize;
BOOL FitInBuffer = TRUE;
SERVERNAME QueueName; // OEM queue name
LPWSTR UQueueName = NULL; // Unicode queue name
DWORD LastObjectId = (DWORD) ContextHandle->ResumeId;
while ( EntriesRequested > *EntriesRead &&
( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) {
RtlZeroMemory(QueueName, sizeof(QueueName));
//
// Call the scan bindery object NCP to scan for all file
// volume objects.
//
status = NwGetNextQueueEntry(
ContextHandle->TreeConnectionHandle,
&LastObjectId,
QueueName
);
if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) {
//
// Pack server name into output buffer.
//
status = NwWritePrinterInfoEntry(
&FixedPortion,
&EndOfVariableData,
ContextHandle->ContainerName,
UQueueName,
PRINTER_ENUM_ICON8,
&EntrySize
);
switch ( status )
{
case ERROR_INSUFFICIENT_BUFFER:
FitInBuffer = FALSE;
// Falls through
case NO_ERROR:
*BytesNeeded += EntrySize;
(*EntriesRead)++;
ContextHandle->ResumeId = (DWORD_PTR) LastObjectId;
break;
default:
break;
}
(void) LocalFree((HLOCAL) UQueueName);
}
}
//
// This is incompliance with the wierd provider API definition where
// if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
// at least one entry fit into output buffer, there's no telling if
// the buffer was too small for more entries or there are no more
// entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
// before knowing that the last call had actually reached the end of list.
//
if ( !FitInBuffer ) {
*EntriesRead = 0;
status = ERROR_INSUFFICIENT_BUFFER;
}
else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
status = NO_ERROR;
}
return status;
}
DWORD
NwrCloseEnum(
IN OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function closes an enum context handle.
Arguments:
EnumHandle - Supplies a pointer to the enum context handle.
Return Value:
WN_BAD_HANDLE - Handle is not recognizable.
NO_ERROR - Call was successful.
--*/ // NwrCloseEnum
{
LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) *EnumHandle;
DWORD status = NO_ERROR ;
#if DBG
IF_DEBUG(ENUM)
{
KdPrint(("\nNWWORKSTATION: NwrCloseEnum\n"));
}
#endif
if (ContextHandle->Signature != NW_HANDLE_SIGNATURE)
{
ASSERT(FALSE);
return WN_BAD_HANDLE;
}
//
// Resume handle for listing directories is a buffer which contains
// the last directory returned.
//
if (ContextHandle->HandleType == NwsHandleListDirectories &&
ContextHandle->ResumeId != 0 &&
ContextHandle->ResumeId != (DWORD_PTR) -1)
{
(void) LocalFree((HLOCAL) ContextHandle->ResumeId);
}
//
// NdsRawDataBuffer handle for listing NDS tree subordinates is a buffer which contains
// the last data chunk returned from redirector.
//
if ( ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk ||
ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print ||
ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any ||
ContextHandle->HandleType == NwsHandleListServersAndNdsTrees ) &&
ContextHandle->NdsRawDataBuffer )
{
(void) LocalFree((HLOCAL) ContextHandle->NdsRawDataBuffer);
ContextHandle->NdsRawDataBuffer = 0;
}
if (ContextHandle->TreeConnectionHandle != (HANDLE) NULL)
{
if (ContextHandle->HandleType == NwsHandleListDirectories)
{
//
// Delete the UNC connection created so that we can browse
// directories.
//
(void) NwNukeConnection(ContextHandle->TreeConnectionHandle, TRUE);
}
if ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk ||
ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print ||
ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any )
{
//
// Get rid of the connection to the NDS tree.
//
(void) CloseHandle(ContextHandle->TreeConnectionHandle);
ContextHandle->TreeConnectionHandle = 0;
}
else
{
(void) NtClose(ContextHandle->TreeConnectionHandle);
ContextHandle->TreeConnectionHandle = 0;
}
}
ContextHandle->Signature = 0x0BADBAD0;
(void) LocalFree((HLOCAL) ContextHandle);
*EnumHandle = NULL;
return status;
}
DWORD
NwrGetUser(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR lpName,
OUT LPBYTE lpUserName,
IN DWORD dwUserNameBufferSize,
OUT LPDWORD lpdwCharsRequired
)
/*++
Routine Description:
This is used to determine either the current default username, or the
username used to establish a network connection.
Arguments:
Reserved - Unused.
lpName - The connection for which user information is requested.
lpUserName - The buffer to receive the user name associated with the
connection referred to by lpName.
dwUserNameLen - The size of the buffer lpUserName.
lpdwCharsRequired - If return status is WN_MORE_DATA, then this is set to
the value which indicates the number of characters that the buffer
lpUserName must hold. Otherwise, this is not set.
Return Value:
WN_SUCCESS - If the call is successful. Otherwise, an error code is,
returned, which may include:
WN_NOT_CONNECTED - lpName not a redirected device nor a connected network
name.
WN_MORE_DATA - The buffer is too small.
--*/ // NwrGetUser
{
DWORD status = NO_ERROR;
WCHAR lpTempUserName[512];
WCHAR lpTempHostName[512];
if (lpName == NULL)
{
return ERROR_INVALID_PARAMETER;
}
status = NwGetConnectionInformation( lpName, lpTempUserName, lpTempHostName );
if ( status == ERROR_BAD_NETPATH )
{
return WN_NOT_CONNECTED;
}
if ( status != NO_ERROR )
{
return status;
}
if ( ( ( wcslen( lpTempUserName ) + 1 ) * sizeof(WCHAR) ) > dwUserNameBufferSize )
{
*lpdwCharsRequired = wcslen( lpTempUserName ) + 1;
return WN_MORE_DATA;
}
wcscpy( (LPWSTR) lpUserName, lpTempUserName );
return WN_SUCCESS;
}
DWORD
NwrGetResourceInformation(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR lpRemoteName,
IN DWORD dwType,
OUT LPBYTE lpBuffer,
IN DWORD dwBufferSize,
OUT LPDWORD lpdwBytesNeeded,
OUT LPDWORD lpdwSystemOffset
)
/*++
Routine Description:
This function returns an object which details information
about a specified network resource.
Arguments:
Reserved - Unused.
lpRemoteName - The full path name to be verified.
dwType - The type of the value, if the calling client knows it.
lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry.
dwBufferSize - The size of the buffer.
lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned.
lpdwSystemOffset - A DWORD that is an offset value to the beginning of a
string that specifies the part of the resource that is accessed through
resource type specific APIs rather than WNet APIs. The string is stored
in the same buffer as the returned NETRESOURCE structure, lpBuffer.
Return Value:
WN_SUCCESS - If the call is successful.
WN_MORE_DATA - If input buffer is too small.
WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
of parameters is specified (e.g. lpRemoteName does not correspond
to dwType).
WN_BAD_NETNAME - The resource is not recognized by this provider.
--*/ // NwrGetResourceInformation
{
DWORD status = NO_ERROR;
DWORD EntrySize;
LPBYTE FixedPortion = lpBuffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD));
LPWSTR lpObjectPathName = NULL;
LPWSTR lpSystemPathPart = NULL;
LPWSTR lpSystem = NULL;
DWORD ClassType;
DWORD ResourceScope = RESOURCE_CONTEXT; // prefix issue
DWORD ResourceType = 0;
DWORD ResourceDisplayType;
DWORD ResourceUsage;
BOOL fReturnBadNetName = FALSE;
if (lpRemoteName == NULL)
{
return ERROR_INVALID_PARAMETER;
}
*lpdwSystemOffset = 0;
status = NwGetNDSPathInfo( lpRemoteName,
&lpObjectPathName,
&lpSystemPathPart,
&ClassType,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage );
if ( status == VERIFY_ERROR_NOT_A_NDS_TREE )
{
//
// Code to handle \\SERVER\VOL\... here!
//
status = NwGetBinderyPathInfo( lpRemoteName,
&lpObjectPathName,
&lpSystemPathPart,
&ClassType,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage );
}
if ( status == VERIFY_ERROR_PATH_NOT_FOUND )
{
fReturnBadNetName = TRUE;
status = NO_ERROR;
}
if ( status == NO_ERROR &&
dwType != RESOURCETYPE_ANY &&
ResourceType != RESOURCETYPE_ANY &&
dwType != ResourceType )
{
status = WN_BAD_VALUE;
}
if ( status == NO_ERROR )
{
//
// Pack subtree name into output buffer.
//
status = NwWriteNetResourceEntry( &FixedPortion,
&EndOfVariableData,
NULL,
NULL,
lpObjectPathName == NULL ? NwProviderName : lpObjectPathName,
ResourceScope,
ResourceDisplayType,
ResourceUsage,
ResourceType,
lpSystemPathPart,
&lpSystem,
&EntrySize );
if ( lpObjectPathName )
(void) LocalFree( (HLOCAL) lpObjectPathName );
}
else
{
if ( lpSystemPathPart != NULL )
{
(void) LocalFree( (HLOCAL) lpSystemPathPart );
lpSystemPathPart = NULL;
}
return status;
}
if ( status != NO_ERROR )
{
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer.
//
*lpdwBytesNeeded = EntrySize;
}
if ( lpSystemPathPart != NULL )
{
(void) LocalFree( (HLOCAL) lpSystemPathPart );
lpSystemPathPart = NULL;
}
if ( fReturnBadNetName )
return WN_BAD_NETNAME;
return status;
}
else
{
LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer;
//
// Replace pointers to strings with offsets as need
//
if (NetR->lpLocalName != NULL)
{
NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) (NetR->lpLocalName) - (DWORD_PTR) lpBuffer);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) (NetR->lpRemoteName) - (DWORD_PTR) lpBuffer);
if (NetR->lpComment != NULL)
{
NetR->lpComment = (LPWSTR) ((DWORD_PTR) (NetR->lpComment) - (DWORD_PTR) lpBuffer);
}
if (NetR->lpProvider != NULL)
{
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) (NetR->lpProvider) - (DWORD_PTR) lpBuffer);
}
if (lpSystem != NULL)
{
*lpdwSystemOffset = (DWORD)((DWORD_PTR) lpSystem - (DWORD_PTR) lpBuffer);
}
if ( lpSystemPathPart != NULL )
{
(void) LocalFree( (HLOCAL) lpSystemPathPart );
lpSystemPathPart = NULL;
}
if ( fReturnBadNetName )
return WN_BAD_NETNAME;
return WN_SUCCESS;
}
}
DWORD
NwrGetResourceParent(
IN LPWSTR Reserved OPTIONAL,
IN LPWSTR lpRemoteName,
IN DWORD dwType,
OUT LPBYTE lpBuffer,
IN DWORD dwBufferSize,
OUT LPDWORD lpdwBytesNeeded
)
/*++
Routine Description:
This function returns an object which details information
about the parent of a specified network resource.
Arguments:
Reserved - Unused.
lpRemoteName - The full path name of object to find the parent of.
dwType - The type of the value, if the calling client knows it.
lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry.
dwBufferSize - The size of the buffer.
lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned.
Return Value:
WN_SUCCESS - If the call is successful.
WN_MORE_DATA - If input buffer is too small.
WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
of parameters is specified (e.g. lpRemoteName does not correspond
to dwType).
--*/ // NwrGetResourceParent
{
DWORD status = NO_ERROR;
DWORD EntrySize;
LPBYTE FixedPortion = lpBuffer;
LPWSTR EndOfVariableData = (LPWSTR) ((DWORD_PTR) FixedPortion +
ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD));
LPWSTR lpRemoteNameParent = NULL;
LPWSTR lpFullObjectPathName = NULL;
DWORD ClassType;
DWORD ResourceScope;
DWORD ResourceType;
DWORD ResourceDisplayType;
DWORD ResourceUsage;
BOOL fReturnBadNetName = FALSE;
if (lpRemoteName == NULL)
{
return ERROR_INVALID_PARAMETER;
}
if ( ! NwGetRemoteNameParent( lpRemoteName, &lpRemoteNameParent ) )
{
return WN_BAD_NETNAME;
}
status = NwVerifyNDSObject( lpRemoteNameParent,
&lpFullObjectPathName,
&ClassType,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage );
if ( status == VERIFY_ERROR_NOT_A_NDS_TREE )
{
status = NwVerifyBinderyObject( lpRemoteNameParent,
&lpFullObjectPathName,
&ClassType,
&ResourceScope,
&ResourceType,
&ResourceDisplayType,
&ResourceUsage );
}
if ( lpRemoteNameParent )
(void) LocalFree( (HLOCAL) lpRemoteNameParent );
if ( status == VERIFY_ERROR_PATH_NOT_FOUND )
{
fReturnBadNetName = TRUE;
status = NO_ERROR;
}
if ( status == NO_ERROR )
{
//
// Pack subtree name into output buffer.
//
status = NwWriteNetResourceEntry( &FixedPortion,
&EndOfVariableData,
NULL,
NULL,
lpFullObjectPathName == NULL ? NwProviderName : lpFullObjectPathName,
ResourceScope,
ResourceDisplayType,
ResourceUsage,
ResourceType,
NULL,
NULL,
&EntrySize );
if ( lpFullObjectPathName )
(void) LocalFree( (HLOCAL) lpFullObjectPathName );
}
else
{
return status;
}
if ( status != NO_ERROR )
{
if (status == WN_MORE_DATA)
{
//
// Could not write current entry into output buffer.
//
*lpdwBytesNeeded = EntrySize;
}
if ( fReturnBadNetName )
return WN_BAD_NETNAME;
return status;
}
else
{
LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer;
//
// Replace pointers to strings with offsets as need
//
if (NetR->lpLocalName != NULL)
{
NetR->lpLocalName = (LPWSTR) ((DWORD_PTR) (NetR->lpLocalName) - (DWORD_PTR) lpBuffer);
}
NetR->lpRemoteName = (LPWSTR) ((DWORD_PTR) (NetR->lpRemoteName) - (DWORD_PTR) lpBuffer);
if (NetR->lpComment != NULL)
{
NetR->lpComment = (LPWSTR) ((DWORD_PTR) (NetR->lpComment) - (DWORD_PTR) lpBuffer);
}
if (NetR->lpProvider != NULL)
{
NetR->lpProvider = (LPWSTR) ((DWORD_PTR) (NetR->lpProvider) - (DWORD_PTR) lpBuffer);
}
if ( fReturnBadNetName )
return WN_BAD_NETNAME;
return WN_SUCCESS;
}
}
VOID
NWWKSTA_CONTEXT_HANDLE_rundown(
IN NWWKSTA_CONTEXT_HANDLE EnumHandle
)
/*++
Routine Description:
This function is called by RPC when a client terminates with an
opened handle. This allows us to clean up and deallocate any context
data associated with the handle.
Arguments:
EnumHandle - Supplies the handle opened for an enumeration.
Return Value:
None.
--*/
{
//
// Call our close handle routine.
//
NwrCloseEnum(&EnumHandle);
}
DWORD
NwGetFirstNdsSubTreeEntry(
OUT LPNW_ENUM_CONTEXT ContextHandle,
IN DWORD BufferSize
)
/*++
Routine Description:
This function is called by NwEnumNdsSubTrees to get the first
subtree entry given a handle to a NDS tree. It allocates
the output buffer to hold the returned subtree name; the
caller should free this output buffer with LocalFree when done.
Arguments:
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
buffer.
Other errors from NwNdsList.
--*/ // NwGetFirstNdsSubTreeEntry
{
NTSTATUS ntstatus;
ContextHandle->NdsRawDataSize = BufferSize;
//
// Determine size of NDS raw data buffer to use.
//
if ( ContextHandle->NdsRawDataSize < EIGHT_KB )
ContextHandle->NdsRawDataSize = EIGHT_KB;
else // dfergus 19 Apr 2001 - 346859
// if buffer too big, set to max NDS buffer size
if (ContextHandle->NdsRawDataSize > 0xFC00) // NW_MAX_BUFFER = 0xFC00
ContextHandle->NdsRawDataSize = 0xFC00;
//
// Create NDS raw data buffer.
//
ContextHandle->NdsRawDataBuffer = (DWORD_PTR)
LocalAlloc( LMEM_ZEROINIT,
ContextHandle->NdsRawDataSize );
if ( ContextHandle->NdsRawDataBuffer == 0 )
{
KdPrint(("NWWORKSTATION: NwGetFirstNdsSubTreeEntry LocalAlloc Failed %lu\n", GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Set up to get initial NDS subordinate list.
//
ContextHandle->NdsRawDataId = INITIAL_ITERATION;
ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle,
ContextHandle->dwOid,
&ContextHandle->NdsRawDataId,
(LPBYTE) ContextHandle->NdsRawDataBuffer,
ContextHandle->NdsRawDataSize );
//
// If error, clean up the ContextHandle and return.
//
if ( ntstatus != STATUS_SUCCESS ||
((PNDS_RESPONSE_SUBORDINATE_LIST)
ContextHandle->NdsRawDataBuffer)->SubordinateEntries == 0 )
{
if ( ContextHandle->NdsRawDataBuffer )
(void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
ContextHandle->NdsRawDataBuffer = 0;
ContextHandle->NdsRawDataSize = 0;
ContextHandle->NdsRawDataId = INITIAL_ITERATION;
ContextHandle->NdsRawDataCount = 0;
ContextHandle->ResumeId = 0;
return WN_NO_MORE_ENTRIES;
}
ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST)
ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1;
ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer +
sizeof( NDS_RESPONSE_SUBORDINATE_LIST );
// Multi-user code merge
// 12/05/96 cjc Fix problem with FileManager not showing all the NDS entries.
// Problem occurs when the NDS entries don't fit in 1 NCP packet;
// need to keep track of the Iteration # and redo NCP.
ContextHandle->NdsRawDataId = ((PNDS_RESPONSE_SUBORDINATE_LIST)
ContextHandle->NdsRawDataBuffer)->IterationHandle;
return RtlNtStatusToDosError(ntstatus);
}
DWORD
NwGetNextNdsSubTreeEntry(
OUT LPNW_ENUM_CONTEXT ContextHandle
)
/*++
Routine Description:
This function is called by NwEnumNdsSubTrees to get the next
NDS subtree entry given a handle to a NDS tree. It allocates
the output buffer to hold the returned subtree name; the
caller should free this output buffer with LocalFree when done.
Arguments:
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
buffer.
Other errors from NwNdsList.
--*/ // NwGetNextDirectoryEntry
{
NTSTATUS ntstatus = STATUS_SUCCESS;
PBYTE pbRaw;
DWORD dwStrLen;
if ( ContextHandle->NdsRawDataCount == 0 &&
ContextHandle->NdsRawDataId == INITIAL_ITERATION )
return WN_NO_MORE_ENTRIES;
if ( ContextHandle->NdsRawDataCount == 0 &&
ContextHandle->NdsRawDataId != INITIAL_ITERATION )
{
ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle,
ContextHandle->dwOid,
&ContextHandle->NdsRawDataId,
(LPBYTE) ContextHandle->NdsRawDataBuffer,
ContextHandle->NdsRawDataSize );
//
// If error, clean up the ContextHandle and return.
//
if (ntstatus != STATUS_SUCCESS)
{
if ( ContextHandle->NdsRawDataBuffer )
(void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
ContextHandle->NdsRawDataBuffer = 0;
ContextHandle->NdsRawDataSize = 0;
ContextHandle->NdsRawDataId = INITIAL_ITERATION;
ContextHandle->NdsRawDataCount = 0;
return WN_NO_MORE_ENTRIES;
}
ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST)
ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1;
ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer +
sizeof( NDS_RESPONSE_SUBORDINATE_LIST );
// ---Multi-user change ---
// 12/05/96 cjc Fix problem with FileManager not showing all the NDS entries.
// Problem occurs when the NDS entries don't fit in 1 NCP packet;
// need to keep track of the Iteration # and redo NCP.
ContextHandle->NdsRawDataId = ((PNDS_RESPONSE_SUBORDINATE_LIST)
ContextHandle->NdsRawDataBuffer)->IterationHandle;
return RtlNtStatusToDosError(ntstatus);
}
ContextHandle->NdsRawDataCount--;
//
// Move pointer past the fixed header portion of a NDS_RESPONSE_SUBORDINATE_ENTRY
//
pbRaw = (BYTE *) ContextHandle->ResumeId;
pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY );
//
// Move pointer past the length value of the Class Name string
// of a NDS_RESPONSE_SUBORDINATE_ENTRY
//
dwStrLen = * (DWORD *) pbRaw;
pbRaw += sizeof( DWORD );
//
// Move pointer past the Class Name string of a NDS_RESPONSE_SUBORDINATE_ENTRY
//
pbRaw += ROUNDUP4( dwStrLen );
//
// Move pointer past the length value of the Object Name string
// of a NDS_RESPONSE_SUBORDINATE_ENTRY
//
dwStrLen = * (DWORD *) pbRaw;
pbRaw += sizeof( DWORD );
ContextHandle->ResumeId = (DWORD_PTR) ( pbRaw + ROUNDUP4( dwStrLen ) );
return RtlNtStatusToDosError(ntstatus);
}
BYTE
NwGetSubTreeData(
IN DWORD_PTR NdsRawDataPtr,
OUT LPWSTR * SubTreeName,
OUT LPDWORD ResourceScope,
OUT LPDWORD ResourceType,
OUT LPDWORD ResourceDisplayType,
OUT LPDWORD ResourceUsage,
OUT LPWSTR * StrippedObjectName
)
/*++
Routine Description:
This function is called by NwEnumNdsSubTrees to get the information
needed to describe a single NETRESOURCE from an entry in the
NdsRawDataBuffer.
Arguments:
NdsRawDataPtr - Supplies the pointer to a buffer with the NDS raw data.
SubTreeName - Receives a pointer to the returned subtree object name
found in buffer.
ResourceScope - Receives the value of the scope for the subtree object
found in buffer.
ResourceType - Receives the value of the type for the subtree object
found in buffer.
ResourceDisplayType - Receives the value of the display type for the
subtree object found in buffer.
ResourceUsage - Receives the value of the usage for the subtree object
found in buffer.
StrippedObjectName - A pointer to receive the address of a buffer which
will contain the formatted object name. Callee must
free buffer with LocalFree().
Return Value:
A DWORD with a value that is used to represent NDS object class type..
--*/ // NwGetSubTreeData
{
PNDS_RESPONSE_SUBORDINATE_ENTRY pSubEntry =
(PNDS_RESPONSE_SUBORDINATE_ENTRY) NdsRawDataPtr;
PBYTE pbRaw;
DWORD dwStrLen;
LPWSTR ClassNameStr;
pbRaw = (BYTE *) pSubEntry;
//
// The structure of a NDS_RESPONSE_SUBORDINATE_ENTRY consists of 4 DWORDs
// followed by two standard NDS format UNICODE strings. Below we jump pbRaw
// into the buffer, past the 4 DWORDs.
//
pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY );
//
// Now we get the length of the first string (Base Class).
//
dwStrLen = * (DWORD *) pbRaw;
//
// Now we point pbRaw to the first WCHAR of the first string (Base Class).
//
pbRaw += sizeof( DWORD_PTR );
ClassNameStr = (LPWSTR) pbRaw;
//
// Move pbRaw into the buffer, past the first UNICODE string (WORD aligned)
//
pbRaw += ROUNDUP4( dwStrLen );
//
// Now we get the length of the second string (Entry Name).
//
dwStrLen = * (DWORD *) pbRaw;
//
// Now we point pbRaw to the first WCHAR of the second string (Entry Name).
//
pbRaw += sizeof( DWORD_PTR );
*SubTreeName = (LPWSTR) pbRaw;
//
// Strip off any CN= stuff from the object name.
//
NwStripNdsUncName( *SubTreeName, StrippedObjectName );
*ResourceScope = RESOURCE_GLOBALNET;
if ( !wcscmp( ClassNameStr, CLASS_NAME_ALIAS ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_ALIAS;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_AFP_SERVER ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_AFP_SERVER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_OBJECT ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_BINDERY_OBJECT;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_QUEUE ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_BINDERY_QUEUE;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_COMPUTER ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_COMPUTER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_COUNTRY ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
*ResourceUsage = RESOURCEUSAGE_CONTAINER;
return CLASS_TYPE_COUNTRY;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_DIRECTORY_MAP ) )
{
*ResourceType = RESOURCETYPE_DISK;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
#ifdef NT1057
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER;
#else
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE;
#endif
return CLASS_TYPE_DIRECTORY_MAP;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_GROUP ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GROUP;
*ResourceUsage = 0;
return CLASS_TYPE_GROUP;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_LOCALITY ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_LOCALITY;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_NCP_SERVER ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER;
*ResourceUsage = RESOURCEUSAGE_CONTAINER;
return CLASS_TYPE_NCP_SERVER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATION ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
*ResourceUsage = RESOURCEUSAGE_CONTAINER;
return CLASS_TYPE_ORGANIZATION;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_ROLE ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_ORGANIZATIONAL_ROLE;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_UNIT ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
*ResourceUsage = RESOURCEUSAGE_CONTAINER;
return CLASS_TYPE_ORGANIZATIONAL_UNIT;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINTER ) )
{
*ResourceType = RESOURCETYPE_PRINT;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE;
return CLASS_TYPE_PRINTER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINT_SERVER ) )
{
*ResourceType = RESOURCETYPE_PRINT;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER;
*ResourceUsage = RESOURCEUSAGE_CONTAINER;
return CLASS_TYPE_PRINT_SERVER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_PROFILE ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_PROFILE;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_QUEUE ) )
{
*ResourceType = RESOURCETYPE_PRINT;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE;
return CLASS_TYPE_QUEUE;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_TOP ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_TOP;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_USER ) )
{
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_USER;
}
if ( !wcscmp( ClassNameStr, CLASS_NAME_VOLUME ) )
{
*ResourceType = RESOURCETYPE_DISK;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
#ifdef NT1057
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER;
#else
*ResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE;
#endif
return CLASS_TYPE_VOLUME;
}
//
// Otherwise if ClassNameStr is something other than Unknown, report it
//
if ( wcscmp( ClassNameStr, CLASS_NAME_UNKNOWN ) )
{
KdPrint(("NWWORKSTATION: NwGetSubTreeData failed to recognize"));
KdPrint((" ClassName: %S\n", ClassNameStr));
KdPrint((" Setting object attributes to Unknown for now . . .\n"));
}
*ResourceType = RESOURCETYPE_ANY;
*ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
*ResourceUsage = 0;
return CLASS_TYPE_UNKNOWN;
}
VOID
NwStripNdsUncName(
IN LPWSTR ObjectName,
OUT LPWSTR * StrippedObjectName
)
{
WORD slashCount;
BOOL isNdsUnc;
LPWSTR FourthSlash;
LPWSTR TreeName;
LPWSTR ObjectPath;
DWORD TreeNameLen;
DWORD ObjectPathLen;
DWORD PrefixBytes;
DWORD CurrentPathIndex;
DWORD StrippedNameLen;
DWORD StrippedNameMaxLen = MAX_NDS_NAME_CHARS;
WCHAR StrippedName[MAX_NDS_NAME_CHARS];
*StrippedObjectName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
(wcslen(ObjectName) + 1) *
sizeof(WCHAR) );
if ( *StrippedObjectName == NULL )
{
return;
}
NwpGetUncInfo( ObjectName, &slashCount, &isNdsUnc, &FourthSlash );
if ( slashCount >= 2 )
{
TreeNameLen = NwParseNdsUncPath( &TreeName,
ObjectName,
PARSE_NDS_GET_TREE_NAME );
TreeNameLen /= sizeof(WCHAR);
wcscpy( *StrippedObjectName, L"\\\\" );
wcsncat( *StrippedObjectName, TreeName, TreeNameLen );
ObjectPathLen = NwParseNdsUncPath( &ObjectPath,
ObjectName,
PARSE_NDS_GET_PATH_NAME );
if ( ObjectPathLen == 0 )
{
_wcsupr( *StrippedObjectName );
return;
}
wcscat( *StrippedObjectName, L"\\" );
}
else
{
wcscpy( *StrippedObjectName, L"" );
ObjectPath = ObjectName;
ObjectPathLen = wcslen(ObjectName) * sizeof(WCHAR);
}
CurrentPathIndex = 0;
PrefixBytes = 0;
StrippedNameLen = 0;
//
// All of these indexes are in BYTES, not WCHARS!
//
while ( ( CurrentPathIndex < ObjectPathLen ) &&
( StrippedNameLen < StrippedNameMaxLen ) )
{
if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'=' )
{
CurrentPathIndex += sizeof( WCHAR );
StrippedNameLen -= PrefixBytes;
PrefixBytes = 0;
continue;
}
StrippedName[StrippedNameLen / sizeof( WCHAR )] =
ObjectPath[CurrentPathIndex / sizeof( WCHAR )];
StrippedNameLen += sizeof( WCHAR );
CurrentPathIndex += sizeof( WCHAR );
if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'.' )
{
PrefixBytes = 0;
PrefixBytes -= sizeof( WCHAR );
}
else
{
PrefixBytes += sizeof( WCHAR );
}
}
StrippedName[StrippedNameLen / sizeof( WCHAR )] = L'\0';
wcscat( *StrippedObjectName, StrippedName );
_wcsupr( *StrippedObjectName );
}
DWORD
NwVerifyNDSObject(
IN LPWSTR lpNDSObjectNamePath,
OUT LPWSTR * lpFullNDSObjectNamePath,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
)
{
DWORD status = NO_ERROR;
NTSTATUS ntstatus = STATUS_SUCCESS;
UNICODE_STRING TreeServerName;
UNICODE_STRING PathString;
HANDLE ConnectionHandle = NULL;
DWORD dwHandleType;
DWORD dwOid;
BOOL fImpersonate = FALSE ;
if ( lpNDSObjectNamePath == NULL )
{
//
// Handle this as if we are at the root of our provider hierarchy.
//
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = RESOURCETYPE_ANY;
#ifdef NT1057
*lpResourceDisplayType = 0;
#else
*lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
#endif
*lpResourceUsage = RESOURCEUSAGE_CONTAINER;
*lpFullNDSObjectNamePath = NULL;
return NO_ERROR;
}
TreeServerName.Buffer = NULL;
PathString.Buffer = NULL;
TreeServerName.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR );
PathString.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR );
TreeServerName.Length = NwParseNdsUncPath( (LPWSTR *) &TreeServerName.Buffer,
lpNDSObjectNamePath,
PARSE_NDS_GET_TREE_NAME );
if ( TreeServerName.Length == 0 || TreeServerName.Buffer == NULL )
{
//
// lpNDSObjectNamePath is not in the form \\name[\blah.blah.blah][\foo][\bar]...
//
status = WN_BAD_NETNAME;
goto ErrorExit;
}
//
// Impersonate the client
//
if ( ( status = NwImpersonateClient() ) != NO_ERROR )
{
goto ErrorExit;
}
fImpersonate = TRUE;
//
// Open a connection handle to \\name
//
ntstatus = NwNdsOpenGenericHandle( &TreeServerName,
&dwHandleType,
&ConnectionHandle );
if ( ntstatus != STATUS_SUCCESS )
{
//
// The first part of lpNDSObjectNamePath was neither a NDS tree nor a NCP Server.
//
status = WN_BAD_NETNAME;
goto ErrorExit;
}
if ( dwHandleType != HANDLE_TYPE_NDS_TREE )
{
//
// The first part of lpNDSObjectNamePath was not a NDS tree.
//
status = VERIFY_ERROR_NOT_A_NDS_TREE;
goto ErrorExit;
}
//
// Adjust TreeServerName.Length to number of characters.
//
TreeServerName.Length /= sizeof(WCHAR);
//
// The lpNDSObjectNamePath points to a NDS tree. Now verify that the path is valid.
//
PathString.Length = NwParseNdsUncPath( (LPWSTR *) &PathString.Buffer,
lpNDSObjectNamePath,
PARSE_NDS_GET_PATH_NAME );
if ( PathString.Length == 0 )
{
LPWSTR treeNameStr = NULL;
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( ConnectionHandle )
CloseHandle( ConnectionHandle );
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = RESOURCETYPE_ANY;
*lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE;
*lpResourceUsage = RESOURCEUSAGE_CONTAINER;
//
// Need to build a string with the new NDS UNC path for subtree object
//
treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( TreeServerName.Length + 3 ) * sizeof(WCHAR) );
if ( treeNameStr == NULL )
{
KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy( treeNameStr, L"\\\\" );
wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length );
_wcsupr( treeNameStr );
*lpFullNDSObjectNamePath = treeNameStr;
return NO_ERROR;
}
else
{
WCHAR lpServerName[NW_MAX_SERVER_LEN];
UNICODE_STRING ServerName;
ServerName.Length = 0;
ServerName.MaximumLength = sizeof( lpServerName );
ServerName.Buffer = lpServerName;
//
// Resolve the path to get a NDS object id.
//
ntstatus = NwNdsResolveName( ConnectionHandle,
&PathString,
&dwOid,
&ServerName,
NULL,
0 );
if ( ntstatus == STATUS_SUCCESS && ServerName.Length )
{
DWORD dwHandleType;
//
// NwNdsResolveName succeeded, but we were referred to
// another server, though ContextHandle->dwOid is still valid.
if ( ConnectionHandle )
CloseHandle( ConnectionHandle );
ConnectionHandle = NULL;
//
// Open a NDS generic connection handle to \\ServerName
//
ntstatus = NwNdsOpenGenericHandle( &ServerName,
&dwHandleType,
&ConnectionHandle );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
ASSERT( dwHandleType != HANDLE_TYPE_NCP_SERVER );
}
}
if ( ntstatus != STATUS_SUCCESS )
{
LPWSTR treeNameStr = NULL;
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = RESOURCETYPE_ANY;
*lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE;
*lpResourceUsage = RESOURCEUSAGE_CONTAINER;
//
// Need to build a string with the new NDS UNC path for subtree object
//
treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( TreeServerName.Length + 3 ) * sizeof(WCHAR) );
if ( treeNameStr == NULL )
{
KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy( treeNameStr, L"\\\\" );
wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length );
_wcsupr( treeNameStr );
*lpFullNDSObjectNamePath = treeNameStr;
status = VERIFY_ERROR_PATH_NOT_FOUND;
goto ErrorExit;
}
//
// Check to see what kind of object is pointed to by lpRemoteName.
//
{
BYTE RawResponse[TWO_KB];
PBYTE pbRawGetInfo;
DWORD RawResponseSize = sizeof(RawResponse);
DWORD dwStrLen;
LPWSTR TreeObjectName;
LPWSTR StrippedObjectName = NULL;
LPWSTR newPathStr = NULL;
ntstatus = NwNdsReadObjectInfo( ConnectionHandle,
dwOid,
RawResponse,
RawResponseSize );
if ( ntstatus != NO_ERROR )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
//
// Get current subtree data from ContextHandle
//
*lpClassType = NwGetSubTreeData( (DWORD_PTR) RawResponse,
&TreeObjectName,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage,
&StrippedObjectName );
if ( StrippedObjectName == NULL )
{
KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
//
// Need to build a string with the new NDS UNC path for subtree object
//
newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( wcslen( StrippedObjectName ) +
TreeServerName.Length + 4 )
* sizeof(WCHAR) );
if ( newPathStr == NULL )
{
(void) LocalFree((HLOCAL) StrippedObjectName);
KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy( newPathStr, L"\\\\" );
wcsncat( newPathStr, TreeServerName.Buffer, TreeServerName.Length );
wcscat( newPathStr, L"\\" );
wcscat( newPathStr, StrippedObjectName );
_wcsupr( newPathStr );
//
// Don't need the StrippedObjectName string anymore
//
(void) LocalFree((HLOCAL) StrippedObjectName);
StrippedObjectName = NULL;
*lpFullNDSObjectNamePath = newPathStr;
status = NO_ERROR;
} // End of Block
ErrorExit:
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( ConnectionHandle )
CloseHandle( ConnectionHandle );
return status;
}
DWORD
NwVerifyBinderyObject(
IN LPWSTR lpBinderyObjectPathName,
OUT LPWSTR * lpFullBinderyObjectPathName,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
)
{
DWORD status = NO_ERROR;
HANDLE ConnectionHandle = NULL;
BOOL fImpersonate = FALSE ;
BOOL fResourceTypeDisk = FALSE ;
BOOL fIsNdsUnc = FALSE ;
UNICODE_STRING BinderyConnectStr;
ULONG CreateDisposition = 0;
ULONG CreateOptions = 0;
WORD wSlashCount;
LPWSTR FourthSlash;
if ( lpBinderyObjectPathName == NULL )
{
//
// Handle this as if we are at the root of our provider hierarchy.
//
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = RESOURCETYPE_ANY;
#ifdef NT1057
*lpResourceDisplayType = 0;
#else
*lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
#endif
*lpResourceUsage = RESOURCEUSAGE_CONTAINER;
*lpFullBinderyObjectPathName = NULL;
return NO_ERROR;
}
//
// Open a connection handle to \\server\vol\...
//
BinderyConnectStr.Buffer = NULL;
//
// Find out if we are looking at a \\server, \\server\vol, or
// \\server\vol\dir . . .
//
NwpGetUncInfo( lpBinderyObjectPathName,
&wSlashCount,
&fIsNdsUnc,
&FourthSlash );
if ( wSlashCount > 2 )
fResourceTypeDisk = TRUE;
//
// Impersonate the client
//
if ( ( status = NwImpersonateClient() ) != NO_ERROR )
{
goto ErrorExit;
}
fImpersonate = TRUE;
//
// Open a tree connection handle to \Device\NwRdr\ContainerName
//
status = NwCreateTreeConnectName( lpBinderyObjectPathName,
NULL,
&BinderyConnectStr );
if ( status != NO_ERROR )
{
status = WN_BAD_NETNAME;
goto ErrorExit;
}
CreateDisposition = FILE_OPEN;
CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
status = NwOpenCreateConnection( &BinderyConnectStr,
NULL,
NULL,
lpBinderyObjectPathName,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
CreateDisposition,
CreateOptions,
RESOURCETYPE_DISK, // When connecting beyond servername
&ConnectionHandle,
NULL );
if ( status == NO_ERROR )
{
LPWSTR BinderyNameStr = NULL;
//
// Need to build a string with the new UNC path for bindery object
//
BinderyNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( wcslen( lpBinderyObjectPathName ) + 1 )
* sizeof(WCHAR) );
if ( BinderyNameStr == NULL )
{
KdPrint(("NWWORKSTATION: NwVerifyBinderyObject LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
wcscpy( BinderyNameStr, lpBinderyObjectPathName );
_wcsupr( BinderyNameStr );
*lpFullBinderyObjectPathName = BinderyNameStr;
if ( BinderyConnectStr.Buffer )
(void) LocalFree((HLOCAL) BinderyConnectStr.Buffer);
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( ConnectionHandle )
{
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = fResourceTypeDisk ?
RESOURCETYPE_DISK :
RESOURCETYPE_ANY;
*lpResourceDisplayType = fResourceTypeDisk ?
RESOURCEDISPLAYTYPE_SHARE :
RESOURCEDISPLAYTYPE_SERVER;
#ifdef NT1057
*lpResourceUsage = fResourceTypeDisk ?
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER :
RESOURCEUSAGE_CONTAINER;
#else
*lpResourceUsage = fResourceTypeDisk ?
RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE :
RESOURCEUSAGE_CONTAINER;
#endif
CloseHandle( ConnectionHandle );
}
return NO_ERROR;
}
ErrorExit:
*lpFullBinderyObjectPathName = NULL;
if ( BinderyConnectStr.Buffer )
(void) LocalFree((HLOCAL) BinderyConnectStr.Buffer);
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( ConnectionHandle )
CloseHandle( ConnectionHandle );
return WN_BAD_NETNAME;
}
DWORD
NwGetNDSPathInfo(
IN LPWSTR lpNDSObjectNamePath,
OUT LPWSTR * lppSystemObjectNamePath,
OUT LPWSTR * lpSystemPathPart,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
)
{
DWORD status = NO_ERROR;
WORD slashCount;
BOOL isNdsUnc;
BOOL fReturnBadNetName = FALSE;
LPWSTR FourthSlash;
LPWSTR lpSystemPath = NULL;
*lpSystemPathPart = NULL;
NwpGetUncInfo( lpNDSObjectNamePath,
&slashCount,
&isNdsUnc,
&FourthSlash );
if ( slashCount <= 3 )
{
//
// Path is to a possible NDS object, check to see if so and if valid...
//
status = NwVerifyNDSObject( lpNDSObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
*lpSystemPathPart = NULL;
return status;
}
else
{
//
// Path is to a directory, see if directory exists . . .
//
status = NwVerifyBinderyObject( lpNDSObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
}
if ( status == WN_BAD_NETNAME )
{
fReturnBadNetName = TRUE;
status = NO_ERROR;
}
if ( status == NO_ERROR )
{
WCHAR TempNDSObjectNamePath[256];
//
// Test \\tree\obj.obj... component and
// return network resource for valid parent and the string,
// lpSystemPathPart, for the directory part ( \dir1\...).
//
if ( *lppSystemObjectNamePath != NULL )
{
(void) LocalFree( (HLOCAL) (*lppSystemObjectNamePath) );
*lppSystemObjectNamePath = NULL;
}
lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
( wcslen( FourthSlash ) + 1 ) *
sizeof( WCHAR ) );
if ( lpSystemPath == NULL )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy( lpSystemPath, FourthSlash );
*FourthSlash = L'\0';
wcscpy( TempNDSObjectNamePath, lpNDSObjectNamePath );
*FourthSlash = L'\\';
//
// See if \\tree\obj.obj.... exists . . .
//
status = NwVerifyNDSObject( TempNDSObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
if ( status != NO_ERROR )
{
LocalFree( lpSystemPath );
lpSystemPath = NULL;
}
}
*lpSystemPathPart = lpSystemPath;
//
// The provider spec for this function used to tell us to create a
// NETRESOURCE, even if the system part of the path was invalid, while
// returning WN_BAD_NETNAME. Now we return SUCCESS and the NETRESOURCE,
// irregardless of whether the lpSystem part is valid.
// if ( fReturnBadNetName == TRUE )
// {
// return WN_BAD_NETNAME;
// }
return status;
}
DWORD
NwGetBinderyPathInfo(
IN LPWSTR lpBinderyObjectNamePath,
OUT LPWSTR * lppSystemObjectNamePath,
OUT LPWSTR * lpSystemPathPart,
OUT LPDWORD lpClassType,
OUT LPDWORD lpResourceScope,
OUT LPDWORD lpResourceType,
OUT LPDWORD lpResourceDisplayType,
OUT LPDWORD lpResourceUsage
)
{
DWORD status = NO_ERROR;
WORD slashCount;
BOOL isNdsUnc;
LPWSTR FourthSlash;
LPWSTR lpSystemPath = NULL;
*lpSystemPathPart = NULL;
NwpGetUncInfo( lpBinderyObjectNamePath,
&slashCount,
&isNdsUnc,
&FourthSlash );
if ( slashCount <= 3 )
{
//
// Path is to a server or volume, check to see which and if valid . . .
//
status = NwVerifyBinderyObject( lpBinderyObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
*lpSystemPathPart = NULL;
return status;
}
else
{
//
// Path is to a directory, see if directory exists . . .
//
status = NwVerifyBinderyObject( lpBinderyObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
}
if ( status == WN_BAD_NETNAME )
{
WCHAR TempBinderyObjectNamePath[256];
//
// Path is to a invalid directory. Test \\server\volume component and
// return network resource for valid parent and the string,
// lpSystemPathPart, for the directory part ( \dir1\...).
//
lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
( wcslen( FourthSlash ) + 1 ) *
sizeof( WCHAR ) );
if ( lpSystemPath == NULL )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy( lpSystemPath, FourthSlash );
*FourthSlash = L'\0';
wcscpy( TempBinderyObjectNamePath, lpBinderyObjectNamePath );
*FourthSlash = L'\\';
//
// See if \\server\volume exists . . .
//
status = NwVerifyBinderyObject( TempBinderyObjectNamePath,
lppSystemObjectNamePath,
lpClassType,
lpResourceScope,
lpResourceType,
lpResourceDisplayType,
lpResourceUsage );
if ( status != NO_ERROR )
{
LocalFree( lpSystemPath );
lpSystemPath = NULL;
}
//
// Return SUCCESS, since the NETRESOURCE for \\server\volume that
// we are describing is at least valid, even though the lpSystem
// part in not. This is a change in the provider spec (4/25/96).
//
// else
// {
// status = WN_BAD_NETNAME;
// }
}
else
{
//
// Path is to a valid directory. Return resource information for the
// \\server\volume component and the string, lpSystemPathPart, for the
// directory part ( \dir1\...).
//
NwpGetUncInfo( *lppSystemObjectNamePath,
&slashCount,
&isNdsUnc,
&FourthSlash );
lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
( wcslen( FourthSlash ) + 1 ) *
sizeof( WCHAR ) );
if ( lpSystemPath == NULL )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy( lpSystemPath, FourthSlash );
*FourthSlash = L'\0';
*lpResourceScope = RESOURCE_GLOBALNET;
*lpResourceType = RESOURCETYPE_DISK;
*lpResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
#ifdef NT1057
*lpResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_CONTAINER;
#else
*lpResourceUsage = RESOURCEUSAGE_CONNECTABLE |
RESOURCEUSAGE_NOLOCALDEVICE;
#endif
status = NO_ERROR;
}
*lpSystemPathPart = lpSystemPath;
return status;
}
BOOL
NwGetRemoteNameParent(
IN LPWSTR lpRemoteName,
OUT LPWSTR * lpRemoteNameParent
)
{
unsigned short iter = 0;
unsigned short totalLength = (USHORT) wcslen( lpRemoteName );
unsigned short slashCount = 0;
unsigned short dotCount = 0;
unsigned short thirdSlash = 0;
unsigned short lastSlash = 0;
unsigned short parentNDSSubTree = 0;
LPWSTR newRemoteNameParent = NULL;
if ( totalLength < 2 )
return FALSE;
//
// Get thirdSlash to indicate the character in the string that indicates the
// "\" in between the tree name and the rest of the UNC path. Set parentNDSSubTree
// if available. And always set lastSlash to the most recent "\" seen as you walk.
//
// Example: \\<tree name>\path.to.object[\|.]<object>
// ^ ^
// | |
// thirdSlash parentNDSSubTree
//
while ( iter < totalLength )
{
if ( lpRemoteName[iter] == L'\\' )
{
slashCount += 1;
if ( slashCount == 3 )
thirdSlash = iter;
lastSlash = iter;
}
if ( lpRemoteName[iter] == L'.' )
{
dotCount += 1;
if ( dotCount == 1 )
parentNDSSubTree = iter;
}
iter++;
}
if ( slashCount > 3 )
{
newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( lastSlash + 1 ) *
sizeof(WCHAR));
if ( newRemoteNameParent == NULL )
{
KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
GetLastError()));
return FALSE;
}
wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash );
_wcsupr( newRemoteNameParent );
*lpRemoteNameParent = newRemoteNameParent;
return TRUE;
}
if ( slashCount == 3 )
{
if ( dotCount == 0 )
{
newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( lastSlash + 1 ) *
sizeof(WCHAR));
if ( newRemoteNameParent == NULL )
{
KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
GetLastError()));
return FALSE;
}
wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash );
_wcsupr( newRemoteNameParent );
*lpRemoteNameParent = newRemoteNameParent;
return TRUE;
}
else
{
newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
( totalLength -
( parentNDSSubTree - thirdSlash )
+ 1 )
* sizeof(WCHAR) );
if ( newRemoteNameParent == NULL )
{
KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
GetLastError()));
return FALSE;
}
wcsncpy( newRemoteNameParent, lpRemoteName, thirdSlash + 1 );
wcscat( newRemoteNameParent, &lpRemoteName[parentNDSSubTree+1] );
_wcsupr( newRemoteNameParent );
*lpRemoteNameParent = newRemoteNameParent;
return TRUE;
}
}
// Else we set lpRemoteNameParent to NULL, to indicate that we are at the top and
// return TRUE.
*lpRemoteNameParent = NULL;
return TRUE;
}
DWORD
NwGetFirstDirectoryEntry(
IN HANDLE DirHandle,
OUT LPWSTR *DirEntry
)
/*++
Routine Description:
This function is called by NwEnumDirectories to get the first
directory entry given a handle to the directory. It allocates
the output buffer to hold the returned directory name; the
caller should free this output buffer with LocalFree when done.
Arguments:
DirHandle - Supplies the opened handle to the container
directory find a directory within it.
DirEntry - Receives a pointer to the returned directory
found.
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
buffer.
Other errors from NtQueryDirectoryFile.
--*/ // NwGetFirstDirectoryEntry
{
DWORD status = NO_ERROR;
NTSTATUS ntstatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_DIRECTORY_INFORMATION DirInfo;
UNICODE_STRING StartFileName;
#if DBG
DWORD i = 0;
#endif
//
// Allocate a large buffer to get one directory information entry.
//
DirInfo = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(FILE_DIRECTORY_INFORMATION) +
(MAX_PATH * sizeof(WCHAR))
);
if (DirInfo == NULL) {
KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlInitUnicodeString(&StartFileName, L"*");
ntstatus = NtQueryDirectoryFile(
DirHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
DirInfo,
sizeof(FILE_DIRECTORY_INFORMATION) +
(MAX_PATH * sizeof(WCHAR)),
FileDirectoryInformation, // Info class requested
TRUE, // Return single entry
&StartFileName, // Redirector needs this
TRUE // Restart scan
);
//
// For now, if buffer to NtQueryDirectoryFile is too small, just give
// up. We may want to try to reallocate a bigger buffer at a later time.
//
if (ntstatus == STATUS_SUCCESS) {
ntstatus = IoStatusBlock.Status;
}
if (ntstatus != STATUS_SUCCESS) {
if (ntstatus == STATUS_NO_MORE_FILES) {
//
// We ran out of entries.
//
status = WN_NO_MORE_ENTRIES;
}
else {
KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
ntstatus));
status = RtlNtStatusToDosError(ntstatus);
}
goto CleanExit;
}
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i,
DirInfo->FileName, DirInfo->FileAttributes));
}
#endif
//
// Scan until we find the first directory entry that is not "." or ".."
//
while (!(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
memcmp(DirInfo->FileName, L".", DirInfo->FileNameLength) == 0 ||
memcmp(DirInfo->FileName, L"..", DirInfo->FileNameLength) == 0) {
ntstatus = NtQueryDirectoryFile(
DirHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
DirInfo,
sizeof(FILE_DIRECTORY_INFORMATION) +
(MAX_PATH * sizeof(WCHAR)),
FileDirectoryInformation, // Info class requested
TRUE, // Return single entry
NULL,
FALSE // Restart scan
);
if (ntstatus == STATUS_SUCCESS) {
ntstatus = IoStatusBlock.Status;
}
if (ntstatus != STATUS_SUCCESS) {
if (ntstatus == STATUS_NO_MORE_FILES) {
//
// We ran out of entries.
//
status = WN_NO_MORE_ENTRIES;
}
else {
KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
ntstatus));
status = RtlNtStatusToDosError(ntstatus);
}
goto CleanExit;
}
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i,
DirInfo->FileName, DirInfo->FileAttributes));
}
#endif
}
//
// Allocate the output buffer for the returned directory name
//
*DirEntry = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
DirInfo->FileNameLength + sizeof(WCHAR)
);
if (*DirEntry == NULL) {
KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry returns %ws\n",
*DirEntry));
}
#endif
status = NO_ERROR;
CleanExit:
(void) LocalFree((HLOCAL) DirInfo);
//
// We could not find any directories under the requested
// so we need to treat this as no entries.
//
if ( status == ERROR_FILE_NOT_FOUND )
status = WN_NO_MORE_ENTRIES;
return status;
}
DWORD
NwGetNextDirectoryEntry(
IN HANDLE DirHandle,
OUT LPWSTR *DirEntry
)
/*++
Routine Description:
This function is called by NwEnumDirectories to get the next
directory entry given a handle to the directory. It allocates
the output buffer to hold the returned directory name; the
caller should free this output buffer with LocalFree when done.
Arguments:
DirHandle - Supplies the opened handle to the container
directory find a directory within it.
DirEntry - Receives a pointer to the returned directory
found.
Return Value:
NO_ERROR - The operation was successful.
ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
buffer.
Other errors from NtQueryDirectoryFile.
--*/ // NwGetNextDirectoryEntry
{
DWORD status = NO_ERROR;
NTSTATUS ntstatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_DIRECTORY_INFORMATION DirInfo;
//
// Allocate a large buffer to get one directory information entry.
//
DirInfo = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
sizeof(FILE_DIRECTORY_INFORMATION) +
(MAX_PATH * sizeof(WCHAR))
);
if (DirInfo == NULL) {
KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
do {
ntstatus = NtQueryDirectoryFile(
DirHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
DirInfo,
sizeof(FILE_DIRECTORY_INFORMATION) +
(MAX_PATH * sizeof(WCHAR)),
FileDirectoryInformation, // Info class requested
TRUE, // Return single entry
NULL,
FALSE // Restart scan
);
if (ntstatus == STATUS_SUCCESS) {
ntstatus = IoStatusBlock.Status;
}
} while (ntstatus == STATUS_SUCCESS &&
!(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY));
if (ntstatus != STATUS_SUCCESS) {
if (ntstatus == STATUS_NO_MORE_FILES) {
//
// We ran out of entries.
//
status = WN_NO_MORE_ENTRIES;
}
else {
KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
ntstatus));
status = RtlNtStatusToDosError(ntstatus);
}
goto CleanExit;
}
//
// Allocate the output buffer for the returned directory name
//
*DirEntry = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
DirInfo->FileNameLength + sizeof(WCHAR)
);
if (*DirEntry == NULL) {
KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n",
GetLastError()));
status = ERROR_NOT_ENOUGH_MEMORY;
goto CleanExit;
}
memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength);
#if DBG
IF_DEBUG(ENUM) {
KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry returns %ws\n",
*DirEntry));
}
#endif
status = NO_ERROR;
CleanExit:
(void) LocalFree((HLOCAL) DirInfo);
return status;
}
DWORD
NwWriteNetResourceEntry(
IN OUT LPBYTE * FixedPortion,
IN OUT LPWSTR * EndOfVariableData,
IN LPWSTR ContainerName OPTIONAL,
IN LPWSTR LocalName OPTIONAL,
IN LPWSTR RemoteName,
IN DWORD ScopeFlag,
IN DWORD DisplayFlag,
IN DWORD UsageFlag,
IN DWORD ResourceType,
IN LPWSTR SystemPath OPTIONAL,
OUT LPWSTR * lppSystem OPTIONAL,
OUT LPDWORD EntrySize
)
/*++
Routine Description:
This function packages a NETRESOURCE entry into the user output buffer.
It is called by the various enum resource routines.
Arguments:
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 NETRESOURCE 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
user 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.
ContainerName - Supplies the full path qualifier to make RemoteName
a full UNC name.
LocalName - Supplies the local device name, if any.
RemoteName - Supplies the remote resource name.
ScopeFlag - Supplies the flag which indicates whether this is a
CONNECTED or GLOBALNET resource.
DisplayFlag - Supplies the flag which tells the UI how to display
the resource.
UsageFlag - Supplies the flag which indicates that the RemoteName
is either a container or a connectable resource or both.
SystemPath - Supplies the optional system path data to be stored in the
NETRESOURCE buffer. This is used by the NPGetResourceInformation
helper routines.
lppSystem - If SystemPath is provided, this will point to the location
in the NETRESOURCE buffer that contains the system path string.
EntrySize - Receives the size of the NETRESOURCE entry in bytes.
Return Value:
NO_ERROR - Successfully wrote entry into user buffer.
ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
WN_MORE_DATA - Buffer was too small to fit entry.
--*/ // NwWriteNetResourceEntry
{
BOOL FitInBuffer = TRUE;
LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion;
LPWSTR RemoteBuffer;
LPWSTR lpSystem;
*EntrySize = sizeof(NETRESOURCEW) +
(wcslen(RemoteName) + wcslen(NwProviderName) + 2) *
sizeof(WCHAR);
if (ARGUMENT_PRESENT(LocalName)) {
*EntrySize += (wcslen(LocalName) + 1) * sizeof(WCHAR);
}
if (ARGUMENT_PRESENT(ContainerName)) {
*EntrySize += wcslen(ContainerName) * sizeof(WCHAR);
}
if (ARGUMENT_PRESENT(SystemPath)) {
*EntrySize += wcslen(SystemPath) * sizeof(WCHAR);
}
*EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD);
//
// See if buffer is large enough to fit the entry.
//
if ((LPWSTR) ( *FixedPortion + *EntrySize) > *EndOfVariableData) {
return WN_MORE_DATA;
}
NetR->dwScope = ScopeFlag;
NetR->dwType = ResourceType;
NetR->dwDisplayType = DisplayFlag;
NetR->dwUsage = UsageFlag;
NetR->lpComment = NULL;
//
// Update fixed entry pointer to next entry.
//
(*FixedPortion) += sizeof(NETRESOURCEW);
//
// RemoteName
//
if (ARGUMENT_PRESENT(ContainerName)) {
//
// Prefix the RemoteName with its container name making the
// it a fully-qualified UNC name.
//
RemoteBuffer = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
(wcslen(RemoteName) + wcslen(ContainerName) + 1) *
sizeof(WCHAR)
);
if (RemoteBuffer == NULL) {
KdPrint(("NWWORKSTATION: NwWriteNetResourceEntry LocalAlloc failed %lu\n",
GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(RemoteBuffer, ContainerName);
wcscat(RemoteBuffer, RemoteName);
}
else {
RemoteBuffer = RemoteName;
}
FitInBuffer = NwlibCopyStringToBuffer(
RemoteBuffer,
wcslen(RemoteBuffer),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&NetR->lpRemoteName
);
if (ARGUMENT_PRESENT(ContainerName)) {
(void) LocalFree((HLOCAL) RemoteBuffer);
}
ASSERT(FitInBuffer);
//
// LocalName
//
if (ARGUMENT_PRESENT(LocalName)) {
FitInBuffer = NwlibCopyStringToBuffer(
LocalName,
wcslen(LocalName),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&NetR->lpLocalName
);
ASSERT(FitInBuffer);
}
else {
NetR->lpLocalName = NULL;
}
//
// SystemPath
//
if (ARGUMENT_PRESENT(SystemPath)) {
FitInBuffer = NwlibCopyStringToBuffer(
SystemPath,
wcslen(SystemPath),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&lpSystem
);
ASSERT(FitInBuffer);
}
else {
lpSystem = NULL;
}
if (ARGUMENT_PRESENT(lppSystem)) {
*lppSystem = lpSystem;
}
//
// ProviderName
//
FitInBuffer = NwlibCopyStringToBuffer(
NwProviderName,
wcslen(NwProviderName),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&NetR->lpProvider
);
ASSERT(FitInBuffer);
if (! FitInBuffer) {
return WN_MORE_DATA;
}
return NO_ERROR;
}
DWORD
NwWritePrinterInfoEntry(
IN OUT LPBYTE *FixedPortion,
IN OUT LPWSTR *EndOfVariableData,
IN LPWSTR ContainerName OPTIONAL,
IN LPWSTR RemoteName,
IN DWORD Flags,
OUT LPDWORD EntrySize
)
/*++
Routine Description:
This function packages a PRINTER_INFO_1 entry into the user output buffer.
Arguments:
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 PRINT_INFO_1 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
user 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.
ContainerName - Supplies the full path qualifier to make RemoteName
a full UNC name.
RemoteName - Supplies the remote resource name.
Flags - Supplies the flag which indicates that the RemoteName
is either a container or not and the icon to use.
EntrySize - Receives the size of the PRINTER_INFO_1 entry in bytes.
Return Value:
NO_ERROR - Successfully wrote entry into user buffer.
ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
ERROR_INSUFFICIENT_BUFFER - Buffer was too small to fit entry.
--*/ // NwWritePrinterInfoEntry
{
BOOL FitInBuffer = TRUE;
PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) *FixedPortion;
LPWSTR RemoteBuffer;
*EntrySize = sizeof(PRINTER_INFO_1W) +
( 2 * wcslen(RemoteName) + 2) * sizeof(WCHAR);
if (ARGUMENT_PRESENT(ContainerName)) {
*EntrySize += wcslen(ContainerName) * sizeof(WCHAR);
}
else {
// 3 is for the length of "!\\"
*EntrySize += (wcslen(NwProviderName) + 3) * sizeof(WCHAR);
}
*EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD);
//
// See if buffer is large enough to fit the entry.
//
if ((LPWSTR) (*FixedPortion + *EntrySize) > *EndOfVariableData) {
return ERROR_INSUFFICIENT_BUFFER;
}
pPrinterInfo1->Flags = Flags;
pPrinterInfo1->pComment = NULL;
//
// Update fixed entry pointer to next entry.
//
(*FixedPortion) += sizeof(PRINTER_INFO_1W);
//
// Name
//
if (ARGUMENT_PRESENT(ContainerName)) {
//
// Prefix the RemoteName with its container name making the
// it a fully-qualified UNC name.
//
RemoteBuffer = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
(wcslen(ContainerName) + wcslen(RemoteName)
+ 1) * sizeof(WCHAR) );
if (RemoteBuffer == NULL) {
KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(RemoteBuffer, ContainerName);
wcscat(RemoteBuffer, RemoteName);
}
else {
//
// Prefix the RemoteName with its provider name
//
RemoteBuffer = (PVOID) LocalAlloc(
LMEM_ZEROINIT,
(wcslen(RemoteName) +
wcslen(NwProviderName) + 4)
* sizeof(WCHAR) );
if (RemoteBuffer == NULL) {
KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError()));
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy(RemoteBuffer, NwProviderName );
wcscat(RemoteBuffer, L"!\\\\" );
wcscat(RemoteBuffer, RemoteName);
}
FitInBuffer = NwlibCopyStringToBuffer(
RemoteBuffer,
wcslen(RemoteBuffer),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&pPrinterInfo1->pName );
(void) LocalFree((HLOCAL) RemoteBuffer);
ASSERT(FitInBuffer);
//
// Description
//
FitInBuffer = NwlibCopyStringToBuffer(
RemoteName,
wcslen(RemoteName),
(LPCWSTR) *FixedPortion,
EndOfVariableData,
&pPrinterInfo1->pDescription );
ASSERT(FitInBuffer);
if (! FitInBuffer) {
return ERROR_INSUFFICIENT_BUFFER;
}
return NO_ERROR;
}
int __cdecl
SortFunc(
IN CONST VOID *p1,
IN CONST VOID *p2
)
/*++
Routine Description:
This function is used in qsort to compare the descriptions of
two printer_info_1 structure.
Arguments:
p1 - Points to a PRINTER_INFO_1 structure
p2 - Points to a PRINTER_INFO_1 structure to compare with p1
Return Value:
Same as return value of lstrccmpi.
--*/
{
PRINTER_INFO_1W *pFirst = (PRINTER_INFO_1W *) p1;
PRINTER_INFO_1W *pSecond = (PRINTER_INFO_1W *) p2;
return lstrcmpiW( pFirst->pDescription, pSecond->pDescription );
}
DWORD
NwGetConnectionInformation(
IN LPWSTR lpName,
OUT LPWSTR lpUserName,
OUT LPWSTR lpHostServer
)
{
DWORD status = NO_ERROR;
NTSTATUS ntstatus = STATUS_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
HANDLE hRdr = NULL;
BOOL fImpersonate = FALSE ;
WCHAR OpenString[] = L"\\Device\\Nwrdr\\*";
UNICODE_STRING OpenName;
OEM_STRING OemArg;
UNICODE_STRING ConnectionName;
WCHAR ConnectionBuffer[512];
ULONG BufferSize = 512;
ULONG RequestSize, ReplyLen;
PNWR_REQUEST_PACKET Request;
BYTE *Reply;
PCONN_INFORMATION pConnInfo;
UNICODE_STRING Name;
//
// Allocate buffer space.
//
Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
if ( !Request )
{
status = ERROR_NOT_ENOUGH_MEMORY;
goto ErrorExit;
}
//
// Impersonate the client
//
if ( ( status = NwImpersonateClient() ) != NO_ERROR )
{
goto ErrorExit;
}
fImpersonate = TRUE;
//
// Convert the connect name to unicode.
//
ConnectionName.Length = wcslen( lpName )* sizeof(WCHAR);
ConnectionName.MaximumLength = sizeof( ConnectionBuffer );
ConnectionName.Buffer = ConnectionBuffer;
if (ConnectionName.Length > MAX_NDS_NAME_SIZE)
{
status = ERROR_INVALID_PARAMETER;
goto ErrorExit;
}
wcscpy( ConnectionName.Buffer, lpName );
_wcsupr( ConnectionName.Buffer );
//
// Set up the object attributes.
//
RtlInitUnicodeString( &OpenName, OpenString );
InitializeObjectAttributes( &ObjectAttributes,
&OpenName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
ntstatus = NtOpenFile( &hRdr,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
//
// Fill out the request packet for FSCTL_NWR_GET_CONN_INFO.
//
Request->Parameters.GetConnInfo.ConnectionNameLength = ConnectionName.Length;
RtlCopyMemory( &(Request->Parameters.GetConnInfo.ConnectionName[0]),
ConnectionBuffer,
ConnectionName.Length );
RequestSize = sizeof( Request->Parameters.GetConnInfo ) + ConnectionName.Length;
Reply = ((PBYTE)Request) + RequestSize;
ReplyLen = BufferSize - RequestSize;
ntstatus = NtFsControlFile( hRdr,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_NWR_GET_CONN_INFO,
(PVOID) Request,
RequestSize,
(PVOID) Reply,
ReplyLen );
if ( ntstatus != STATUS_SUCCESS )
{
status = RtlNtStatusToDosError(ntstatus);
goto ErrorExit;
}
(void) NwRevertToSelf() ;
fImpersonate = FALSE;
NtClose( hRdr );
pConnInfo = (PCONN_INFORMATION) Reply;
wcscpy( lpUserName, pConnInfo->UserName );
wcscpy( lpHostServer, pConnInfo->HostServer );
LocalFree( Request );
return NO_ERROR;
ErrorExit:
if ( fImpersonate )
(void) NwRevertToSelf() ;
if ( Request )
LocalFree( Request );
if ( hRdr )
NtClose( hRdr );
return status;
}
VOID
NwpGetUncInfo(
IN LPWSTR lpstrUnc,
OUT WORD * slashCount,
OUT BOOL * isNdsUnc,
OUT LPWSTR * FourthSlash
)
{
WORD i;
WORD length = (WORD) wcslen( lpstrUnc );
*isNdsUnc = (BOOL) FALSE;
*slashCount = 0;
*FourthSlash = NULL;
for ( i = 0; i < length; i++ )
{
if ( lpstrUnc[i] == L'=' )
{
*isNdsUnc = TRUE;
}
if ( lpstrUnc[i] == L'\\' )
{
*slashCount += 1;
if ( *slashCount == 4 )
{
*FourthSlash = &lpstrUnc[i];
}
}
}
}
DWORD
NwpGetCurrentUserRegKey(
IN DWORD DesiredAccess,
OUT HKEY *phKeyCurrentUser
)
/*++
Routine Description:
This routine opens the current user's registry key under
\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWCWorkstation\Parameters
Arguments:
DesiredAccess - The access mask to open the key with
phKeyCurrentUser - Receives the opened key handle
Return Value:
Returns the appropriate Win32 error.
--*/
{
DWORD err;
HKEY hkeyWksta;
LPWSTR CurrentUser;
HKEY hInteractiveLogonKey; //Multi-user
HKEY OneLogonKey; //Multi-user
LUID logonid; //Multi-user
WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN]; //Multi-user
//
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
// \NWCWorkstation\Parameters
//
err = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_WORKSTATION_REGKEY,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&hkeyWksta
);
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters key unexpected error %lu!\n", err));
return err;
}
//
// Impersonate the client
//
if ( ( err = NwImpersonateClient() ) != NO_ERROR ) {
(void) RegCloseKey( hkeyWksta );
return err;
}
//
// Get the NT logon id
//
GetLuid( &logonid );
//
// Revert
//
(void) NwRevertToSelf() ;
// Open interactive user section
err = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_INTERACTIVE_LOGON_REGKEY,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&hInteractiveLogonKey
);
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Interactive logon key unexpected error %lu!\n", err));
(void) RegCloseKey( hkeyWksta );
return err;
}
// Open the logonid
NwLuidToWStr(&logonid, LogonIdKeyName);
err = RegOpenKeyExW(
hInteractiveLogonKey,
LogonIdKeyName,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&OneLogonKey
);
(void) RegCloseKey( hInteractiveLogonKey );
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open logon key unexpected error %lu!\n", err));
(void) RegCloseKey( hkeyWksta );
return err;
}
// Read SID
err = NwReadRegValue(
OneLogonKey,
NW_SID_VALUENAME,
&CurrentUser
);
(void) RegCloseKey( OneLogonKey );
(void) RegCloseKey( hkeyWksta );
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey read user Sid unexpected error %lu!\n", err));
return err;
}
//
// Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
// \NWCWorkstation\Parameters\Option
//
err = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
NW_WORKSTATION_OPTION_REGKEY,
REG_OPTION_NON_VOLATILE,
KEY_READ,
&hkeyWksta
);
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters\\Option key unexpected error %lu!\n", err));
return err;
}
//
// Open current user's key
//
err = RegOpenKeyExW(
hkeyWksta,
CurrentUser,
REG_OPTION_NON_VOLATILE,
DesiredAccess,
phKeyCurrentUser
);
if ( err == ERROR_FILE_NOT_FOUND)
{
DWORD Disposition;
//
// Create <NewUser> key under NWCWorkstation\Parameters\Option
//
err = RegCreateKeyExW(
hkeyWksta,
CurrentUser,
0,
WIN31_CLASS,
REG_OPTION_NON_VOLATILE,
DesiredAccess,
NULL, // security attr
phKeyCurrentUser,
&Disposition
);
}
if ( err ) {
KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open or create of Parameters\\Option\\%ws key failed %lu\n", CurrentUser, err));
}
(void) RegCloseKey( hkeyWksta );
(void) LocalFree((HLOCAL)CurrentUser) ;
return err;
}
DWORD
NwQueryInfo(
OUT LPWSTR *ppszPreferredSrv
)
/*++
Routine Description:
This routine gets the user's preferred server and print options from
the registry.
Arguments:
ppszPreferredSrv - Receives the user's preferred server
Return Value:
Returns the appropriate Win32 error.
--*/
{
HKEY hKeyCurrentUser = NULL;
DWORD BufferSize;
DWORD BytesNeeded;
DWORD ValueType;
LPWSTR PreferredServer ;
DWORD err ;
//
// get to right place in registry and allocate dthe buffer
//
if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
{
//
// If somebody mess around with the registry and we can't find
// the registry, just use the defaults.
//
*ppszPreferredSrv = NULL;
return NO_ERROR;
}
BufferSize = sizeof(WCHAR) * (MAX_PATH + 2) ;
PreferredServer = (LPWSTR) LocalAlloc(LPTR, BufferSize) ;
if (!PreferredServer)
return (GetLastError()) ;
//
// Read PreferredServer value into Buffer.
//
BytesNeeded = BufferSize ;
err = RegQueryValueExW( hKeyCurrentUser,
NW_SERVER_VALUENAME,
NULL,
&ValueType,
(LPBYTE) PreferredServer,
&BytesNeeded );
if (err != NO_ERROR)
{
//
// set to empty and carry on
//
PreferredServer[0] = 0;
}
if (hKeyCurrentUser != NULL)
(void) RegCloseKey(hKeyCurrentUser) ;
*ppszPreferredSrv = PreferredServer ;
return NO_ERROR ;
}