Windows: AFS Redirector Network Provider
authorPeter Scott <pscott@kerneldrivers.com>
Thu, 15 Sep 2011 05:52:08 +0000 (01:52 -0400)
committerJeffrey Altman <jaltman@openafs.org>
Thu, 15 Sep 2011 18:29:08 +0000 (11:29 -0700)
In Windows a network file system must have a matching network
provider dll that interfaces with the Multiple Provider Router
(MPR) to support the WNet APIs called by the Windows Explorer
Shell and other applications.  The WNet APIs are primarily
used to support driver letter mapping but they also have a
number of other functions including universal name mapping,
path formatting, and path parsing.

Jeffrey Altman <jaltman@your-file-system.com> contributed to
the development of the AFSRDFSProvider.dll interface.

Change-Id: I9476003e05f12684676e8c7331a0a8dd13d98686
Reviewed-on: http://gerrit.openafs.org/5439
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Rod Widdowson <rdw@steadingsoftware.com>
Tested-by: Rod Widdowson <rdw@steadingsoftware.com>
Reviewed-by: Jeffrey Altman <jaltman@openafs.org>
Tested-by: Jeffrey Altman <jaltman@openafs.org>

src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj.vspscc [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFS_Npdll.DEF [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFS_Npdll.c [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFS_Npdll.h [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFS_Npdll.rc [new file with mode: 0644]
src/WINNT/afsrdr/npdll/AFS_NpdllMain.C [new file with mode: 0644]
src/WINNT/afsrdr/npdll/SOURCES [new file with mode: 0644]
src/WINNT/afsrdr/npdll/tests/enumresources.c [new file with mode: 0644]

diff --git a/src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj b/src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj
new file mode 100644 (file)
index 0000000..905c317
--- /dev/null
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8.00"
+       Name="AFSRDFS Provider"
+       ProjectGUID="{E191A87E-DAB1-4D1E-9A23-3A1B559258F0}"
+       RootNamespace="NPDll"
+       SccProjectName="SAK"
+       SccAuxPath="SAK"
+       SccLocalPath="SAK"
+       SccProvider="SAK"
+       Keyword="MakeFileProj"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="Debug"
+                       IntermediateDirectory="Debug"
+                       ConfigurationType="0"
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+                       >
+                       <Tool
+                               Name="VCNMakeTool"
+                               BuildCommandLine=""
+                               ReBuildCommandLine=""
+                               CleanCommandLine=""
+                               Output="NPDll.exe"
+                               PreprocessorDefinitions=""
+                               IncludeSearchPath=""
+                               ForcedIncludes=""
+                               AssemblySearchPath=""
+                               ForcedUsingAssemblies=""
+                               CompileAsManaged=""
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="Release"
+                       IntermediateDirectory="Release"
+                       ConfigurationType="0"
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+                       >
+                       <Tool
+                               Name="VCNMakeTool"
+                               BuildCommandLine=""
+                               ReBuildCommandLine=""
+                               CleanCommandLine=""
+                               Output="NPDll.exe"
+                               PreprocessorDefinitions=""
+                               IncludeSearchPath=""
+                               ForcedIncludes=""
+                               AssemblySearchPath=""
+                               ForcedUsingAssemblies=""
+                               CompileAsManaged=""
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath=".\AFS_Npdll.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\AFS_NpdllMain.C"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+               </Filter>
+               <Filter
+                       Name="Resource Files"
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+                       >
+                       <File
+                               RelativePath=".\AFS_Npdll.DEF"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\SOURCES"
+                               >
+                       </File>
+               </Filter>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj.vspscc b/src/WINNT/afsrdr/npdll/AFSRDFSProvder.vcproj.vspscc
new file mode 100644 (file)
index 0000000..f1eb364
--- /dev/null
@@ -0,0 +1,10 @@
+\feff""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:AFSRDFSProvider"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/src/WINNT/afsrdr/npdll/AFS_Npdll.DEF b/src/WINNT/afsrdr/npdll/AFS_Npdll.DEF
new file mode 100644 (file)
index 0000000..64cca3b
--- /dev/null
@@ -0,0 +1,24 @@
+SECTIONS       .AFSNP  READ WRITE SHARED
+
+EXPORTS
+    DllMain                     @1
+    NPLogonNotify               @7
+    NPPasswordChangeNotify      @8
+    NPGetConnection             @12
+    NPGetCaps                   @13
+    NPAddConnection             @17
+    NPCancelConnection          @18
+    NPOpenEnum                  @33
+    NPEnumResource              @34
+    NPCloseEnum                 @35
+    NPFormatNetworkName         @36
+    NPAddConnection3            @38
+    NPGetUniversalName          @40
+    NPGetResourceParent         @41
+    NPGetConnectionPerformance  @49
+    NPGetResourceInformation    @52
+    NPGetConnection3            @54
+
+;    NPGetUser                   @16
+;    NPGetReconnectFlags         @53
+;    I_SystemFocusDialog         @15
diff --git a/src/WINNT/afsrdr/npdll/AFS_Npdll.c b/src/WINNT/afsrdr/npdll/AFS_Npdll.c
new file mode 100644 (file)
index 0000000..56e9ef1
--- /dev/null
@@ -0,0 +1,3951 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
+ * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice,
+ *   this list of conditions and the following disclaimer in the
+ *   documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
+ *   nor the names of their contributors may be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission from Kernel Drivers, LLC and Your File System, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <windows.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <winsvc.h>
+#include <winnetwk.h>
+#include <npapi.h>
+#include <winioctl.h>
+#include <strsafe.h>
+
+#define AFS_DEBUG_TRACE 1
+
+#ifndef WNNC_NET_OPENAFS
+#define WNNC_NET_OPENAFS     0x00390000
+#endif
+
+#include "AFSUserDefines.h"
+#include "AFSUserIoctl.h"
+#include "AFSUserStructs.h"
+#include "AFSProvider.h"
+#include "AFS_Npdll.h"
+
+#include "stdio.h"
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+#define SCRATCHSZ 1024
+
+//
+// Common information
+//
+
+ULONG _cdecl AFSDbgPrint( PWCHAR Format, ... );
+
+#define WNNC_DRIVER( major, minor ) ( major * 0x00010000 + minor )
+
+#define OPENAFS_PROVIDER_NAME           L"OpenAFS Network"
+#define OPENAFS_PROVIDER_NAME_LENGTH    30
+
+#define MAX_PROVIDER_NAME_LENGTH        256
+
+static ULONG cbProviderNameLength = OPENAFS_PROVIDER_NAME_LENGTH;
+
+static wchar_t wszProviderName[MAX_PROVIDER_NAME_LENGTH+1] = OPENAFS_PROVIDER_NAME;
+
+static BOOL bProviderNameRead = FALSE;
+
+#define OPENAFS_SERVER_NAME             L"AFS"
+#define OPENAFS_SERVER_NAME_LENGTH      6
+
+#define OPENAFS_SERVER_COMMENT          L"AFS Root"
+#define OPENAFS_SERVER_COMMENT_LENGTH   16
+
+#define MAX_SERVER_NAME_LENGTH         30
+
+static ULONG cbServerNameLength = 0;
+
+static ULONG cbServerNameUNCLength = 0;
+
+static ULONG cbServerCommentLength = OPENAFS_SERVER_COMMENT_LENGTH;
+
+static wchar_t wszServerName[MAX_SERVER_NAME_LENGTH+1];
+
+static wchar_t wszServerNameUNC[MAX_SERVER_NAME_LENGTH+3];
+
+static wchar_t wszServerComment[] = OPENAFS_SERVER_COMMENT;
+
+static BOOL bServerNameRead = FALSE;
+
+LARGE_INTEGER
+AFSRetrieveAuthId( void);
+
+void
+ReadProviderNameString( void)
+{
+    HKEY hk;
+    DWORD code;
+    DWORD dwLen = 0;
+
+    if ( bProviderNameRead )
+        return;
+
+    code = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+                         L"SYSTEM\\CurrentControlSet\\Services\\AFSRedirector\\NetworkProvider",
+                         0, KEY_QUERY_VALUE, &hk);
+
+    if ( code == ERROR_SUCCESS) {
+
+        dwLen = sizeof(wszProviderName);
+
+        code = RegQueryValueExW( hk, L"Name", NULL, NULL,
+                                 (LPBYTE) wszProviderName, &dwLen);
+
+        if ( code == ERROR_SUCCESS)
+        {
+
+            wszProviderName[MAX_PROVIDER_NAME_LENGTH] = '\0';
+
+            cbProviderNameLength = wcslen( wszProviderName) * sizeof( WCHAR);
+        }
+
+        RegCloseKey( hk);
+    }
+
+    bProviderNameRead = TRUE;
+}
+
+void
+ReadServerNameString( void)
+{
+    HKEY hk;
+    DWORD code;
+    DWORD dwLen = 0;
+
+    if ( bServerNameRead )
+        return;
+
+    code = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+                         L"SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters",
+                         0, KEY_QUERY_VALUE, &hk);
+
+    if ( code == ERROR_SUCCESS) {
+
+        dwLen = sizeof(wszProviderName);
+
+        code = RegQueryValueExW( hk, L"NetbiosName", NULL, NULL,
+                                 (LPBYTE) wszServerName, &dwLen);
+
+        if ( code == ERROR_SUCCESS)
+        {
+
+            wszServerName[MAX_SERVER_NAME_LENGTH] = '\0';
+
+            cbServerNameLength = wcslen( wszServerName) * sizeof( WCHAR);
+
+            wszServerNameUNC[0] = wszServerNameUNC[1] = L'\\';
+
+            memcpy(&wszServerNameUNC[2], wszServerName, (cbServerNameLength + 1) * sizeof( WCHAR));
+
+            cbServerNameUNCLength = cbServerNameLength + 2 * sizeof( WCHAR);
+        }
+
+        RegCloseKey( hk);
+    }
+
+    bServerNameRead = TRUE;
+}
+
+
+
+/* returns TRUE if the file system is disabled or not installed */
+BOOL
+NPIsFSDisabled( void)
+{
+    HKEY hk;
+    DWORD code;
+    DWORD dwLen = 0;
+    DWORD dwStart = SERVICE_DISABLED;
+
+    code = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+                         L"SYSTEM\\CurrentControlSet\\Services\\AFSRedirector",
+                         0, KEY_QUERY_VALUE, &hk);
+
+    if ( code != ERROR_SUCCESS)
+
+        return TRUE;
+
+
+    dwLen = sizeof(dwStart);
+
+    code = RegQueryValueExW( hk, L"Start", NULL, NULL,
+                             (LPBYTE) &dwStart, &dwLen);
+
+    RegCloseKey( hk);
+
+    return ( dwStart == SERVICE_DISABLED);
+}
+
+
+#define try_return(S) { S; goto try_exit; }
+
+#define __Enter
+
+#define NOTHING
+
+typedef struct _UNICODE_STRING {
+    USHORT Length;
+    USHORT MaximumLength;
+    PWSTR Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+HANDLE
+OpenRedirector( void);
+
+typedef struct _AFS_ENUM_CB
+{
+
+    DWORD       CurrentIndex;
+
+    DWORD       Scope;
+
+    DWORD       Type;
+
+    WCHAR      *RemoteName;
+
+} AFSEnumerationCB;
+
+
+//
+// Recursively evaluate drivestr to find the final
+// dos drive letter to which the source is mapped.
+//
+static BOOL
+DriveSubstitution(LPCWSTR drivestr, LPWSTR subststr, size_t substlen)
+{
+    WCHAR drive[3];
+    WCHAR device[MAX_PATH + 1];
+    HRESULT hr = S_OK;
+
+    memset( subststr, 0, substlen);
+    drive[0] = drivestr[0];
+    drive[1] = drivestr[1];
+    drive[2] = 0;
+
+    if ( QueryDosDevice(drive, device, MAX_PATH) )
+    {
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"DriveSubstitution QueryDosDevice %s [%s -> %s]\n",
+                     drivestr,
+                     drive,
+                     device);
+#endif
+        if ( device[0] == L'\\' &&
+             device[1] == L'?' &&
+             device[2] == L'?' &&
+             device[3] == L'\\' &&
+             iswalpha(device[4]) &&
+             device[5] == L':')
+        {
+            drive[0] = device[4];
+            drive[1] = L':';
+            drive[2] = L'\0';
+
+            if ( !DriveSubstitution(drive, subststr, substlen) )
+            {
+
+                subststr[0] = drive[0];
+                subststr[1] = L':';
+                subststr[2] = L'\0';
+
+            }
+
+            hr = S_OK;
+
+            if ( device[6] )
+            {
+                hr = StringCchCat( subststr, substlen, &device[6]);
+            }
+            if ( SUCCEEDED(hr) && drivestr[2] )
+            {
+                hr = StringCchCat( subststr, substlen, &drivestr[2]);
+            }
+
+            if ( SUCCEEDED(hr) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"DriveSubstitution %s -> %s\n",
+                                 drivestr,
+                                 subststr);
+#endif
+                return TRUE;
+            }
+        }
+        else if ( device[0] == L'\\' &&
+                  device[1] == L'?' &&
+                  device[2] == L'?' &&
+                  device[3] == L'\\' &&
+                  device[4] == L'U' &&
+                  device[5] == L'N' &&
+                  device[6] == L'C' &&
+                  device[7] == L'\\')
+        {
+
+            subststr[0] = L'\\';
+
+            hr = StringCbCopyN(&subststr[1], substlen - sizeof(WCHAR), &device[7], MAX_PATH);
+
+            if ( SUCCEEDED(hr) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+            {
+                if ( drivestr[2] )
+                {
+                    hr = StringCchCat( subststr, substlen, &drivestr[2]);
+                }
+                else
+                {
+                    hr = S_OK;
+                }
+
+                if ( SUCCEEDED(hr) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"DriveSubstitution %s -> %s\n",
+                                 drivestr,
+                                 subststr);
+#endif
+                    return TRUE;
+                }
+            }
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"DriveSubstitution StringCbCopyN 1 hr 0x%X\n",
+                         hr);
+#endif
+        }
+        else if ( _wcsnicmp( AFS_RDR_DEVICE_NAME, device, sizeof( AFS_RDR_DEVICE_NAME) / sizeof( WCHAR) - 1) == 0)
+        {
+            //
+            // \Device\AFSRedirector\;X:\\afs\cellname
+            //
+
+            hr = StringCbCopyN( subststr, substlen,
+                                 &device[3 + sizeof( AFS_RDR_DEVICE_NAME) / sizeof( WCHAR)],
+                                 MAX_PATH * sizeof( WCHAR));
+
+            if ( SUCCEEDED(hr) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+            {
+
+                if ( drivestr[2] )
+                {
+                    hr = StringCchCat( subststr, substlen, &drivestr[2]);
+                }
+                else
+                {
+                    hr = S_OK;
+                }
+
+                if ( SUCCEEDED(hr) || hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"DriveSubstitution %s -> %s\n",
+                                 drivestr,
+                                 subststr);
+#endif
+                    return TRUE;
+                }
+            }
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"DriveSubstitution StringCbCopyN 2 hr 0x%X\n",
+                         hr);
+#endif
+        }
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"DriveSubstitution no substitution or match %s !! %s\n",
+                     drivestr,
+                     device);
+#endif
+    }
+    else
+    {
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"DriveSubstitution QueryDosDevice failed %s gle 0x%X\n",
+                     drivestr,
+                     GetLastError());
+#endif
+    }
+
+
+
+    return FALSE;
+}
+
+
+static const WCHAR *
+NPGetCapsQueryString( DWORD nIndex)
+{
+    switch ( nIndex) {
+    case WNNC_SPEC_VERSION:
+        return L"WNNC_SPEC_VERSION";
+
+    case WNNC_NET_TYPE:
+        return L"WNNC_NET_TYPE";
+
+    case WNNC_DRIVER_VERSION:
+        return L"WNNC_DRIVER_VERSION";
+
+    case WNNC_USER:
+        return L"WNNC_USER";
+
+    case WNNC_CONNECTION:
+        return L"WNNC_CONNECTION";
+
+    case WNNC_DIALOG:
+        return L"WNNC_DIALOG";
+
+    case WNNC_ADMIN:
+        return L"WNNC_ADMIN";
+
+    case WNNC_ENUMERATION:
+        return L"WNNC_ENUMERATION";
+
+    case WNNC_START:
+        return L"WNNC_START";
+
+    case WNNC_CONNECTION_FLAGS:
+        return L"WNNC_CONNECTION_FLAGS";
+
+    default:
+        return L"UNKNOWN";
+    }
+}
+
+//
+// This is the only function which must be exported, everything else is optional
+//
+
+DWORD
+APIENTRY
+NPGetCaps( DWORD nIndex )
+{
+
+    DWORD rc = 0;
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPGetCaps Index %u %s\n", nIndex,
+                 NPGetCapsQueryString( nIndex));
+#endif
+    switch( nIndex)
+    {
+        case WNNC_SPEC_VERSION:
+        {
+
+            rc = WNNC_SPEC_VERSION51;
+            break;
+        }
+
+        case WNNC_NET_TYPE:
+        {
+            rc = WNNC_NET_OPENAFS;
+            break;
+        }
+
+        case WNNC_DRIVER_VERSION:
+        {
+
+            rc = WNNC_DRIVER(1, 0);
+            break;
+        }
+
+        case WNNC_CONNECTION:
+        {
+
+            //
+            // No support for:
+            //   WNNC_CON_GETPERFORMANCE
+            //   WNNC_CON_DEFER
+            //
+
+            rc = WNNC_CON_GETCONNECTIONS |
+                 WNNC_CON_CANCELCONNECTION |
+                 WNNC_CON_ADDCONNECTION |
+                 WNNC_CON_ADDCONNECTION3;
+
+            break;
+        }
+
+        case WNNC_ENUMERATION:
+        {
+            rc = WNNC_ENUM_LOCAL |
+                 WNNC_ENUM_CONTEXT |
+                 WNNC_ENUM_GLOBAL |
+                 WNNC_ENUM_SHAREABLE;
+            break;
+        }
+
+        case WNNC_START:
+        {
+
+            rc = WNNC_WAIT_FOR_START;
+
+            break;
+        }
+
+        case WNNC_DIALOG:
+        {
+
+            //
+            // No support for:
+            //    WNNC_DLG_DEVICEMODE
+            //    WNNC_DLG_PROPERTYDIALOG
+            //    WNNC_DLG_SEARCHDIALOG
+            //    WNNC_DLG_PERMISSIONEDITOR
+            //
+
+            rc = WNNC_DLG_FORMATNETWORKNAME |
+                 WNNC_DLG_GETRESOURCEINFORMATION |
+                 WNNC_DLG_GETRESOURCEPARENT;
+
+            break;
+        }
+
+        case WNNC_USER:
+        {
+            //
+            // No support for:
+            //    WNNC_USR_GETUSER
+            //
+
+            break;
+        }
+
+        case WNNC_ADMIN:
+        {
+            //
+            // No support for:
+            //    WNNC_ADM_GETDIRECTORYTYPE
+            //    WNNC_ADM_DIRECTORYNOTIFY
+            // used by the old File Manager
+            //
+            break;
+        }
+    }
+
+    return rc;
+}
+
+DWORD
+APIENTRY
+NPAddConnection( LPNETRESOURCE   lpNetResource,
+                 LPWSTR          lpPassword,
+                 LPWSTR          lpUserName )
+{
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPAddConnection forwarding to NPAddConnection3\n");
+#endif
+    return NPAddConnection3( NULL, lpNetResource, lpPassword, lpUserName, 0 );
+}
+
+DWORD
+APIENTRY
+NPAddConnection3( HWND            hwndOwner,
+                  LPNETRESOURCE   lpNetResource,
+                  LPWSTR          lpPassword,
+                  LPWSTR          lpUserName,
+                  DWORD           dwFlags )
+{
+
+    DWORD    dwStatus = WN_SUCCESS;
+    WCHAR    wchRemoteName[MAX_PATH+1];
+    WCHAR    wchLocalName[3];
+    DWORD    dwCopyBytes = 0;
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    HANDLE   hControlDevice = NULL;
+    HANDLE   hToken = NULL;
+    LARGE_INTEGER liAuthId = {0,0};
+
+    __Enter
+    {
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 AFSRDFS is disabled, returning WN_BAD_NETNAME\n");
+#endif
+
+            return WN_BAD_NETNAME;
+        }
+
+        if ((lpNetResource->lpRemoteName == NULL) ||
+            (lpNetResource->lpRemoteName[0] != L'\\') ||
+            (lpNetResource->lpRemoteName[1] != L'\\') ||
+            ((lpNetResource->dwType != RESOURCETYPE_DISK) &&
+             (lpNetResource->dwType != RESOURCETYPE_ANY)))
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 invalid input, returning WN_BAD_NETNAME\n");
+#endif
+            return WN_BAD_NETNAME;
+        }
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPAddConnection3 processing\n");
+#endif
+        if( lpNetResource->lpLocalName != NULL)
+        {
+
+            wchLocalName[0] = towupper(lpNetResource->lpLocalName[0]);
+            wchLocalName[1] = L':';
+            wchLocalName[2] = L'\0';
+        }
+
+        StringCchCopy(wchRemoteName, MAX_PATH+1, lpNetResource->lpRemoteName);
+        wchRemoteName[MAX_PATH] = L'\0';
+
+        //
+        // Allocate our buffer to pass to the redirector filter
+        //
+
+        dwBufferSize = sizeof( AFSNetworkProviderConnectionCB) + (wcslen( wchRemoteName) * sizeof( WCHAR));
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        if( lpNetResource->lpLocalName != NULL)
+        {
+
+            pConnectCB->LocalName = towupper(wchLocalName[0]);
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 Adding mapping for drive %s remote name %s\n",
+                         wchLocalName,
+                         wchRemoteName);
+#endif
+        }
+        else
+        {
+
+            pConnectCB->LocalName = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 Adding mapping for NO drive remote name %s\n",
+                         wchRemoteName);
+#endif
+        }
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->RemoteNameLength = wcslen( wchRemoteName) * sizeof( WCHAR);
+
+        memcpy( pConnectCB->RemoteName,
+                wchRemoteName,
+                pConnectCB->RemoteNameLength);
+
+        pConnectCB->Type = lpNetResource->dwType;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPAddConnection3 Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_ADD_CONNECTION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   &dwStatus,
+                                   sizeof( DWORD),
+                                   &dwCopyBytes,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPAddConnection3 Failed to add connection to file system %d\n", GetLastError());
+#endif
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        //
+        // The status returned from the driver will indicate how it was handled
+        //
+
+        if( dwStatus == WN_SUCCESS &&
+            lpNetResource->lpLocalName != NULL)
+        {
+
+            WCHAR TempBuf[MAX_PATH+1];
+
+            if( !QueryDosDeviceW( wchLocalName,
+                                  TempBuf,
+                                  MAX_PATH+1))
+            {
+
+                if( GetLastError() != ERROR_FILE_NOT_FOUND)
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPAddConnection3 QueryDosDeviceW failed with file not found\n");
+#endif
+                    NPCancelConnection( wchLocalName, TRUE);
+
+                    dwStatus = ERROR_ALREADY_ASSIGNED;
+                }
+                else
+                {
+
+                    UNICODE_STRING uniConnectionName;
+                    UNICODE_STRING uniDeviceName;
+
+                    uniDeviceName.Length = (wcslen( AFS_RDR_DEVICE_NAME) * sizeof( WCHAR));
+                    uniDeviceName.MaximumLength = uniDeviceName.Length;
+                    uniDeviceName.Buffer = AFS_RDR_DEVICE_NAME;
+
+                    //
+                    // Create a symbolic link object to the device we are redirecting
+                    //
+
+                    uniConnectionName.MaximumLength = (USHORT)( uniDeviceName.Length +
+                                                                pConnectCB->RemoteNameLength +
+                                                                8 +              // Local name and \;
+                                                                sizeof(WCHAR));   //  Space for NULL-termination.
+
+                    //
+                    //  Don't include NULL-termination.
+                    //
+
+                    uniConnectionName.Length = uniConnectionName.MaximumLength - sizeof(WCHAR);
+
+                    uniConnectionName.Buffer = LocalAlloc( LMEM_ZEROINIT,
+                                                           uniConnectionName.MaximumLength);
+
+                    if( uniConnectionName.Buffer == NULL)
+                    {
+
+                        try_return( dwStatus = GetLastError());
+                    }
+
+                    CopyMemory( uniConnectionName.Buffer,
+                                uniDeviceName.Buffer,
+                                uniDeviceName.Length);
+
+                    StringCchCatW( uniConnectionName.Buffer,
+                                   uniConnectionName.MaximumLength,
+                                   L"\\;" );
+
+                    StringCchCatW( uniConnectionName.Buffer,
+                                   uniConnectionName.MaximumLength,
+                                   wchLocalName);
+
+                    StringCchCatW( uniConnectionName.Buffer,
+                                   uniConnectionName.MaximumLength,
+                                   wchRemoteName);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPAddConnection3 DefineDosDevice Local %s connection name %s\n",
+                                 wchLocalName,
+                                 uniConnectionName.Buffer);
+#endif
+
+                    if( !DefineDosDeviceW( DDD_RAW_TARGET_PATH |
+                                           DDD_NO_BROADCAST_SYSTEM,
+                                           wchLocalName,
+                                           uniConnectionName.Buffer))
+                    {
+#ifdef AFS_DEBUG_TRACE
+                        AFSDbgPrint( L"NPAddConnection3 Failed to assign drive\n");
+#endif
+                        dwStatus = GetLastError();
+                    }
+                    else
+                    {
+
+                        dwStatus = WN_SUCCESS;
+                    }
+
+                    LocalFree( uniConnectionName.Buffer);
+                }
+            }
+            else
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPAddConnection3 QueryDosDeviceW %Z already existed\n", TempBuf);
+#endif
+                NPCancelConnection( wchLocalName, TRUE);
+
+                dwStatus = ERROR_ALREADY_ASSIGNED;
+            }
+        }
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwStatus;
+}
+
+DWORD
+APIENTRY
+NPCancelConnection( LPWSTR  lpName,
+                    BOOL    fForce)
+{
+
+    WCHAR    wchRemoteName[MAX_PATH+1];
+    DWORD    dwRemoteNameLength = (MAX_PATH+1) * sizeof(WCHAR);
+    DWORD    dwStatus = WN_NOT_CONNECTED;
+    DWORD    dwCopyBytes = 0;
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    AFSCancelConnectionResultCB stCancelConn;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    BOOL     bLocalName = TRUE;
+    HANDLE   hControlDevice = NULL;
+    WCHAR   *pwchLocalName = NULL;
+
+    __Enter
+    {
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPCancelConnection AFSRDFS is disabled, returning WN_NOT_CONNECTED\n");
+#endif
+
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        if( *lpName == L'\\' &&
+            *(lpName + 1) == L'\\')
+        {
+
+            bLocalName = FALSE;
+        }
+
+        if( bLocalName)
+        {
+
+            //
+            // Get the remote name for the connection, if we are handling it
+            //
+
+            dwStatus = NPGetConnection( lpName,
+                                        wchRemoteName,
+                                        &dwRemoteNameLength);
+
+            if( dwStatus != WN_SUCCESS ||
+                dwRemoteNameLength == 0)
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPCancelConnection Status 0x%x NameLength %u, returning WN_NOT_CONNECTED\n",
+                             dwStatus, dwRemoteNameLength);
+#endif
+                try_return( dwStatus = WN_NOT_CONNECTED);
+            }
+        }
+        else
+        {
+
+            StringCchCopyW( wchRemoteName, MAX_PATH+1, lpName);
+
+            dwRemoteNameLength = (wcslen( wchRemoteName) * sizeof( WCHAR));
+        }
+
+        wchRemoteName[ dwRemoteNameLength/sizeof( WCHAR)] = L'\0';
+
+        dwBufferSize = sizeof( AFSNetworkProviderConnectionCB) + dwRemoteNameLength;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        if( bLocalName)
+        {
+
+            pConnectCB->LocalName = towupper(lpName[0]);
+        }
+        else
+        {
+
+            pConnectCB->LocalName = L'\0';
+        }
+
+        pConnectCB->RemoteNameLength = (USHORT)dwRemoteNameLength;
+
+        StringCchCopyW( pConnectCB->RemoteName,
+                        MAX_PATH+1,
+                        wchRemoteName);
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPCancelConnection Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPCancelConnection OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        memset( &stCancelConn,
+                '\0',
+                sizeof( AFSCancelConnectionResultCB));
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_CANCEL_CONNECTION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   &stCancelConn,
+                                   sizeof( AFSCancelConnectionResultCB),
+                                   &dwCopyBytes,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPCancelConnection Failed to cancel connection to file system - gle 0x%x\n", gle);
+#endif
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        dwStatus = stCancelConn.Status;
+
+#ifdef AFS_DEBUG_TRACE
+
+        AFSDbgPrint( L"NPCancelConnection Cancel connection to file system - Name %s Status %08lX\n",
+                     lpName,
+                     dwStatus);
+#endif
+
+        if( dwStatus == WN_SUCCESS &&
+            ( bLocalName ||
+              stCancelConn.LocalName != L'\0'))
+        {
+
+            UNICODE_STRING uniConnectionName;
+            UNICODE_STRING uniDeviceName;
+
+            uniDeviceName.Length = (wcslen( AFS_RDR_DEVICE_NAME) * sizeof( WCHAR));
+            uniDeviceName.MaximumLength = uniDeviceName.Length;
+            uniDeviceName.Buffer = AFS_RDR_DEVICE_NAME;
+
+            //
+            // Create a symbolic link object to the device we are redirecting
+            //
+
+            uniConnectionName.MaximumLength = (USHORT)( uniDeviceName.Length +
+                                                        dwRemoteNameLength +
+                                                        8 +             // Local name and \;
+                                                        sizeof(WCHAR)); //  Space for NULL-termination.
+
+            //
+            //  Don't include NULL-termination.
+            //
+
+            uniConnectionName.Length = uniConnectionName.MaximumLength - sizeof(WCHAR);
+
+            uniConnectionName.Buffer = LocalAlloc( LMEM_ZEROINIT,
+                                                   uniConnectionName.MaximumLength);
+
+            if( uniConnectionName.Buffer == NULL)
+            {
+
+                try_return( dwStatus = GetLastError());
+            }
+
+            CopyMemory( uniConnectionName.Buffer,
+                        uniDeviceName.Buffer,
+                        uniDeviceName.Length);
+
+            StringCchCatW( uniConnectionName.Buffer,
+                           uniConnectionName.MaximumLength,
+                           L"\\;" );
+
+            if( !bLocalName)
+            {
+
+                WCHAR wchLocalName[ 3];
+
+                wchLocalName[ 0] = stCancelConn.LocalName;
+
+                wchLocalName[ 1] = L':';
+
+                wchLocalName[ 2] = L'\0';
+
+                StringCchCatW( uniConnectionName.Buffer,
+                               uniConnectionName.MaximumLength,
+                               wchLocalName);
+
+                pwchLocalName = wchLocalName;
+            }
+            else
+            {
+
+                StringCchCatW( uniConnectionName.Buffer,
+                               uniConnectionName.MaximumLength,
+                               lpName);
+
+                pwchLocalName = lpName;
+            }
+
+            StringCchCatW( uniConnectionName.Buffer,
+                           uniConnectionName.MaximumLength,
+                           wchRemoteName);
+
+            if( !DefineDosDevice( DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE,
+                                  pwchLocalName,
+                                  uniConnectionName.Buffer))
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                DWORD gle = GetLastError();
+
+                AFSDbgPrint( L"NPCancelConnection Failed to cancel connection to system - gle 0x%x Name %s connection %wZ\n",
+                             gle,
+                             pwchLocalName,
+                             &uniConnectionName);
+#endif
+            }
+            else
+            {
+#ifdef AFS_DEBUG_TRACE
+
+                AFSDbgPrint( L"NPCancelConnection Canceled connection to system - Name %s connection %wZ\n",
+                             pwchLocalName,
+                             &uniConnectionName);
+#endif
+            }
+        }
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+
+    }
+
+    return dwStatus;
+}
+
+DWORD
+APIENTRY
+NPGetConnection( LPWSTR  lpLocalName,
+                 LPWSTR  lpRemoteName,
+                 LPDWORD lpBufferSize)
+{
+
+    DWORD    dwStatus = WN_NOT_CONNECTED;
+    WCHAR    wchLocalName[3];
+    WCHAR    wchSubstName[MAX_PATH + 1];
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    HANDLE   hControlDevice = NULL;
+    DWORD    dwPassedSize;
+
+    __Enter
+    {
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection AFSRDFS is disabled, returning WN_NOT_CONNECTED\n");
+#endif
+
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        if( lstrlen( lpLocalName) == 0)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection No local name, returning WN_BAD_LOCALNAME\n");
+#endif
+            try_return( dwStatus = WN_BAD_LOCALNAME);
+        }
+
+        if ( lpBufferSize == NULL)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection No output size, returning WN_BAD_LOCALNAME\n");
+#endif
+            try_return( dwStatus = WN_BAD_VALUE);
+        }
+
+        dwPassedSize = *lpBufferSize;
+
+        if ( !DriveSubstitution( lpLocalName, wchSubstName, sizeof( wchSubstName)))
+        {
+            wchLocalName[0] = towupper(lpLocalName[0]);
+            wchLocalName[1] = L':';
+            wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection Requesting connection for %s\n",
+                         wchLocalName);
+#endif
+        }
+        else
+        {
+            ReadServerNameString();
+
+            if ( wchSubstName[0] != L'\\' &&
+                 wchSubstName[1] == L':')
+            {
+
+                wchLocalName[0] = towupper(wchSubstName[0]);
+                wchLocalName[1] = L':';
+                wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection Requesting connection for drive substitution %s -> %s\n",
+                             wchSubstName,
+                             wchLocalName);
+#endif
+            }
+            else if ( _wcsnicmp( wchSubstName, wszServerNameUNC, cbServerNameUNCLength / sizeof( WCHAR)) == 0 &&
+                      ( wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == L'\\' ||
+                        wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == 0))
+            {
+                HRESULT hr;
+                WCHAR  *pwch;
+                DWORD   dwCount = 0;
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection drive substitution %s is AFS\n",
+                             wchSubstName);
+#endif
+
+                if ( lpRemoteName == NULL ||
+                     dwPassedSize == 0)
+                {
+
+                    *lpBufferSize = wcslen( wchSubstName) * sizeof( WCHAR) + sizeof( WCHAR);
+
+                    try_return( dwStatus = WN_MORE_DATA);
+
+                }
+
+                hr = StringCbCopyN(lpRemoteName, *lpBufferSize, wchSubstName, sizeof( wchSubstName));
+
+                if ( SUCCEEDED(hr))
+                {
+
+                    for ( dwCount = 0, pwch = lpRemoteName; *pwch; pwch++ )
+                    {
+                        if ( *pwch == L'\\' )
+                        {
+                            dwCount++;
+
+                            if ( dwCount == 4)
+                            {
+                                *pwch = L'\0';
+
+                                break;
+                            }
+                        }
+
+                    }
+
+                    *lpBufferSize = wcslen( lpRemoteName) * sizeof( WCHAR) + sizeof( WCHAR);
+
+                    try_return( dwStatus = WN_SUCCESS);
+                }
+                else if ( hr == STRSAFE_E_INSUFFICIENT_BUFFER)
+                {
+
+                    *lpBufferSize = wcslen( wchSubstName) * sizeof( WCHAR) + sizeof( WCHAR);
+
+                    for ( dwCount = 0, pwch = lpRemoteName; *pwch; pwch++ )
+                    {
+                        if ( *pwch == L'\\' )
+                        {
+                            dwCount++;
+
+                            if ( dwCount == 4)
+                            {
+                                *pwch = L'\0';
+
+                                *lpBufferSize = wcslen( lpRemoteName) * sizeof( WCHAR) + sizeof( WCHAR);
+
+                                try_return( dwStatus = WN_SUCCESS);
+                            }
+                        }
+
+                    }
+
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+                else
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetConnection StringCbCopyN failure 0x%X\n",
+                                 hr);
+#endif
+                    try_return( dwStatus = WN_NET_ERROR);
+                }
+            }
+            else
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection drive substitution %s is not AFS\n",
+                             wchSubstName);
+#endif
+                try_return( dwStatus = WN_NOT_CONNECTED);
+            }
+        }
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetConnection Requesting connection for %s\n",
+                     wchLocalName);
+#endif
+
+        dwBufferSize = 0x1000;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectCB->LocalName = towupper(wchLocalName[0]);
+
+        pConnectCB->RemoteNameLength = 0;
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetConnection Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_GET_CONNECTION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   lpBufferSize,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPGetConnection Failed to get connection from file system for local %s gle 0x%x\n",
+                         wchLocalName, gle);
+#endif
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        //
+        // IOCTL_AFS_GET_CONNECTION returns a counted string
+        //
+
+        if( lpRemoteName == NULL ||
+            *lpBufferSize + sizeof( WCHAR) > dwPassedSize)
+        {
+
+            *lpBufferSize += sizeof( WCHAR);
+
+            try_return( dwStatus = WN_MORE_DATA);
+        }
+
+        memcpy( lpRemoteName,
+                (void *)pConnectCB,
+                *lpBufferSize);
+
+        lpRemoteName[ *lpBufferSize/sizeof( WCHAR)] = L'\0';
+
+        *lpBufferSize += sizeof( WCHAR);
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetConnection local %s remote %s\n",
+                     wchLocalName,
+                     lpRemoteName);
+#endif
+        dwStatus = WN_SUCCESS;
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwStatus;
+}
+
+DWORD
+APIENTRY
+NPGetConnection3( IN     LPCWSTR lpLocalName,
+                  IN     DWORD dwLevel,
+                  OUT    LPVOID lpBuffer,
+                  IN OUT LPDWORD lpBufferSize)
+{
+
+    DWORD    dwStatus = WN_NOT_CONNECTED;
+    WCHAR    wchLocalName[3];
+    WCHAR    wchSubstName[MAX_PATH + 1];
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    HANDLE   hControlDevice = NULL;
+    DWORD    dwPassedSize;
+    DWORD   *pConnectState =(DWORD *)lpBuffer;
+
+    __Enter
+    {
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 AFSRDFS is disabled, returning WN_NOT_CONNECTED\n");
+#endif
+
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        if( lstrlen( lpLocalName) == 0)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 No local name, returning WN_BAD_LOCALNAME\n");
+#endif
+            try_return( dwStatus = WN_BAD_LOCALNAME);
+        }
+
+        //
+        // LanMan NPGetConnection3 only responds to level 1
+        //
+
+        if ( dwLevel != 0x1)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 Level 0x%X returning WN_BAD_LEVEL\n", dwLevel);
+#endif
+            try_return( dwStatus = WN_BAD_LEVEL);
+        }
+
+        if ( lpBufferSize == NULL)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 No output size, returning WN_BAD_VALUE\n");
+#endif
+            try_return( dwStatus = WN_BAD_VALUE);
+        }
+
+        dwPassedSize = *lpBufferSize;
+
+        if ( dwPassedSize == 0 ||
+             lpBuffer == NULL)
+        {
+
+            *lpBufferSize = sizeof( DWORD);
+
+            try_return( dwStatus = WN_MORE_DATA);
+        }
+
+        if ( !DriveSubstitution( lpLocalName, wchSubstName, sizeof( wchSubstName)))
+        {
+            wchLocalName[0] = towupper(lpLocalName[0]);
+            wchLocalName[1] = L':';
+            wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 Requesting connection for %s level 0x%X\n",
+                         wchLocalName,
+                         dwLevel);
+#endif
+        }
+        else
+        {
+
+            ReadServerNameString();
+
+            if ( wchSubstName[0] != L'\\' &&
+                 wchSubstName[1] == L':')
+            {
+
+                wchLocalName[0] = towupper(wchSubstName[0]);
+                wchLocalName[1] = L':';
+                wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection3 Requesting connection for drive substitution %s -> %s level 0x%x\n",
+                             wchSubstName,
+                             wchLocalName,
+                             dwLevel);
+#endif
+            }
+            else if ( _wcsnicmp( wchSubstName, wszServerNameUNC, cbServerNameUNCLength / sizeof( WCHAR)) == 0 &&
+                      ( wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == L'\\' ||
+                        wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == 0))
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection3 drive substitution %s is AFS return connected\n",
+                             wchSubstName);
+#endif
+                *pConnectState = WNGETCON_CONNECTED;
+
+                *lpBufferSize = sizeof( DWORD);
+
+                try_return( dwStatus = WN_SUCCESS);
+            }
+            else
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetConnection3 drive substitution %s is not AFS return not connected\n",
+                             wchSubstName);
+#endif
+                try_return( dwStatus = WN_NOT_CONNECTED);
+            }
+        }
+
+        dwBufferSize = 0x1000;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectCB->LocalName = towupper(wchLocalName[0]);
+
+        pConnectCB->RemoteNameLength = 0;
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetConnection3 Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnection3 OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_GET_CONNECTION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   &dwBufferSize,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPGetConnection3 Failed to get connection from file system for local %s gle 0x%x\n",
+                         wchLocalName, gle);
+#endif
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        *lpBufferSize = sizeof( DWORD);
+
+        if( sizeof( DWORD) > dwPassedSize)
+        {
+
+            try_return( dwStatus = WN_MORE_DATA);
+        }
+
+        *pConnectState = WNGETCON_CONNECTED;
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetConnection3 local %s connect-state 0x%x\n",
+                     wchLocalName,
+                     *pConnectState);
+#endif
+        dwStatus = WN_SUCCESS;
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwStatus;
+}
+
+DWORD
+APIENTRY
+NPGetConnectionPerformance( LPCWSTR lpRemoteName,
+                            LPNETCONNECTINFOSTRUCT lpNetConnectInfo)
+{
+
+    DWORD dwReturn = WN_SUCCESS;
+    AFSNetworkProviderConnectionCB *pConnectCB = NULL;
+    DWORD dwBufferSize = 0;
+    HANDLE hControlDevice = NULL;
+    DWORD dwError = 0;
+
+    __Enter
+    {
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetConnectionPerformance AFSRDFS is disabled, returning WN_BAD_NETNAME\n");
+#endif
+
+            return WN_NO_NETWORK;
+        }
+
+        AFSDbgPrint( L"NPGetConnectionPerformance Entry for remote connection %S\n",
+                     lpRemoteName);
+
+        dwBufferSize = 0x1000;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+            try_return( dwReturn = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectCB->RemoteNameLength = wcslen( lpRemoteName) * sizeof( WCHAR);
+
+        StringCbCopy( pConnectCB->RemoteName,
+                      dwBufferSize - sizeof(AFSNetworkProviderConnectionCB),
+                      lpRemoteName);
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+            AFSDbgPrint( L"NPGetConnectionPerformance OpenRedirector failure, returning WN_NET_ERROR\n");
+
+            try_return( dwReturn = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_GET_CONNECTION_INFORMATION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   &dwBufferSize,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPGetConnectionPerformance Failed to get connection info from file system for remote %S gle 0x%x\n",
+                         lpRemoteName,
+                         gle);
+#endif
+            try_return( dwReturn = WN_NOT_CONNECTED);
+        }
+
+        lpNetConnectInfo->dwFlags = WNCON_DYNAMIC;
+
+        lpNetConnectInfo->dwSpeed = 500;
+
+        lpNetConnectInfo->dwDelay = 0;
+
+        lpNetConnectInfo->dwOptDataSize = 0x1000;
+
+        AFSDbgPrint( L"NPGetConnectionPerformance Successfully returned information for remote connection %S\n",
+                     lpRemoteName);
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwReturn;
+}
+
+static LPCWSTR
+GetUsageString( DWORD dwUsage)
+{
+    static WCHAR Buffer[128] = L"";
+    //
+    // RESOURCEUSAGE_CONNECTABLE   0x00000001
+    // RESOURCEUSAGE_CONTAINER     0x00000002
+    // RESOURCEUSAGE_NOLOCALDEVICE 0x00000004
+    // RESOURCEUSAGE_SIBLING       0x00000008
+    // RESOURCEUSAGE_ATTACHED      0x00000010
+    // RESOURCEUSAGE_ALL           (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED)
+    // RESOURCEUSAGE_RESERVED      0x80000000
+    //
+
+    Buffer[0] = L'\0';
+
+    if ( dwUsage == RESOURCEUSAGE_ALL )
+    {
+        return L"ALL";
+    }
+
+    if ( dwUsage == 0 )
+    {
+        return L"NONE";
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_CONNECTABLE )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"CONNECTABLE|");
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_CONTAINER )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"CONTAINER|");
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_NOLOCALDEVICE )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"NOLOCALDEVICE|");
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_SIBLING )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"SIBLING|");
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_ATTACHED )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"ATTACHED|");
+    }
+
+    if ( dwUsage & RESOURCEUSAGE_RESERVED )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"RESERVED|");
+    }
+
+    if ( dwUsage & ~(RESOURCEUSAGE_ALL|RESOURCEUSAGE_NOLOCALDEVICE|RESOURCEUSAGE_SIBLING|RESOURCEUSAGE_RESERVED) )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"UNKNOWN|");
+    }
+
+    Buffer[lstrlen(Buffer)-1] = L'\0';
+
+    return Buffer;
+}
+
+static LPCWSTR
+GetTypeString( DWORD dwType)
+{
+    static WCHAR Buffer[128] = L"";
+
+    //
+    // RESOURCETYPE_ANY        0x00000000
+    // RESOURCETYPE_DISK       0x00000001
+    // RESOURCETYPE_PRINT      0x00000002
+    // RESOURCETYPE_RESERVED   0x00000008
+    // RESOURCETYPE_UNKNOWN    0xFFFFFFFF
+    //
+
+    Buffer[0] = L'\0';
+
+    if ( dwType == RESOURCETYPE_ANY )
+    {
+        return L"ANY";
+    }
+
+    if ( dwType == RESOURCETYPE_UNKNOWN )
+    {
+        return L"UNKNOWN";
+    }
+
+    if ( dwType & RESOURCETYPE_DISK )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"DISK|");
+    }
+
+    if ( dwType & RESOURCETYPE_PRINT )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"PRINT|");
+    }
+
+    if ( dwType & RESOURCETYPE_RESERVED )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"RESERVED|");
+    }
+
+    if ( dwType & ~(RESOURCETYPE_DISK|RESOURCETYPE_PRINT|RESOURCETYPE_RESERVED) )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"UNKNOWN|");
+    }
+
+    Buffer[lstrlen(Buffer)-1] = L'\0';
+
+    return Buffer;
+}
+
+static LPCWSTR
+GetScopeString( DWORD dwScope)
+{
+    static WCHAR Buffer[128] = L"";
+
+    //
+    // RESOURCE_CONNECTED      0x00000001
+    // RESOURCE_GLOBALNET      0x00000002
+    // RESOURCE_REMEMBERED     0x00000003
+    // RESOURCE_RECENT         0x00000004
+    // RESOURCE_CONTEXT        0x00000005
+    //
+
+    Buffer[0] = L'\0';
+
+    if ( dwScope == RESOURCE_CONNECTED )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"CONNECTED|");
+    }
+
+    if ( dwScope == RESOURCE_GLOBALNET )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"GLOBALNET|");
+    }
+
+    if ( dwScope == RESOURCE_REMEMBERED )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"REMEMBERED|");
+    }
+
+    if ( dwScope == RESOURCE_RECENT )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"RECENT|");
+    }
+
+    if ( dwScope == RESOURCE_CONTEXT )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"CONTEXT|");
+    }
+
+    if ( dwScope & ~(RESOURCE_CONNECTED|RESOURCE_GLOBALNET|RESOURCE_REMEMBERED|RESOURCE_RECENT|RESOURCE_CONTEXT) )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"UNKNOWN|");
+    }
+
+    Buffer[lstrlen(Buffer)-1] = L'\0';
+
+    return Buffer;
+}
+
+static LPCWSTR
+GetDisplayString( DWORD dwDisplay)
+{
+    //
+    // RESOURCEDISPLAYTYPE_GENERIC        0x00000000
+    // RESOURCEDISPLAYTYPE_DOMAIN         0x00000001
+    // RESOURCEDISPLAYTYPE_SERVER         0x00000002
+    // RESOURCEDISPLAYTYPE_SHARE          0x00000003
+    // RESOURCEDISPLAYTYPE_FILE           0x00000004
+    // RESOURCEDISPLAYTYPE_GROUP          0x00000005
+    // RESOURCEDISPLAYTYPE_NETWORK        0x00000006
+    // RESOURCEDISPLAYTYPE_ROOT           0x00000007
+    // RESOURCEDISPLAYTYPE_SHAREADMIN     0x00000008
+    // RESOURCEDISPLAYTYPE_DIRECTORY      0x00000009
+    // RESOURCEDISPLAYTYPE_TREE           0x0000000A
+    // RESOURCEDISPLAYTYPE_NDSCONTAINER   0x0000000B
+    //
+
+    switch ( dwDisplay ) {
+    case RESOURCEDISPLAYTYPE_GENERIC:
+        return L"GENERIC";
+    case RESOURCEDISPLAYTYPE_DOMAIN:
+        return L"DOMAIN";
+    case RESOURCEDISPLAYTYPE_SERVER:
+        return L"SERVER";
+    case RESOURCEDISPLAYTYPE_SHARE:
+        return L"SHARE";
+    case RESOURCEDISPLAYTYPE_FILE:
+        return L"FILE";
+    case RESOURCEDISPLAYTYPE_GROUP:
+        return L"GROUP";
+    case RESOURCEDISPLAYTYPE_NETWORK:
+        return L"NETWORK";
+    case RESOURCEDISPLAYTYPE_ROOT:
+        return L"ROOT";
+    case RESOURCEDISPLAYTYPE_SHAREADMIN:
+        return L"SHAREADMIN";
+    case RESOURCEDISPLAYTYPE_DIRECTORY:
+        return L"DIRECTORY";
+    case RESOURCEDISPLAYTYPE_TREE:
+        return L"TREE";
+    case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+        return L"NDSCONTAINER";
+    default:
+        return L"UNKNOWN";
+    }
+}
+
+DWORD
+APIENTRY
+NPOpenEnum( DWORD          dwScope,
+            DWORD          dwType,
+            DWORD          dwUsage,
+            LPNETRESOURCE  lpNetResource,
+            LPHANDLE       lphEnum )
+{
+
+    DWORD   dwStatus = WN_SUCCESS;
+    AFSEnumerationCB *pEnumCB = NULL;
+
+#ifdef AFS_DEBUG_TRACE
+    if ( lpNetResource == NULL)
+    {
+        AFSDbgPrint( L"NPOpenEnum Scope %s Type %s Usage %s NetResource: (Null)\n",
+                     GetScopeString(dwScope), GetTypeString(dwType), GetUsageString(dwUsage));
+    }
+    else
+    {
+        AFSDbgPrint( L"NPOpenEnum Scope %s Type %s Usage %s NetResource (0x%p): Scope %s Type %s Display %s Usage %s Local %s Remote \"%s\" Comment \"%s\"\n",
+                     GetScopeString(dwScope),
+                     GetTypeString(dwType),
+                     GetUsageString(dwUsage),
+                     lpNetResource,
+                     GetScopeString(lpNetResource->dwScope),
+                     GetTypeString(lpNetResource->dwType),
+                     GetDisplayString(lpNetResource->dwDisplayType),
+                     GetUsageString(lpNetResource->dwUsage),
+                     lpNetResource->lpLocalName,
+                     lpNetResource->lpRemoteName,
+                     lpNetResource->lpComment);
+    }
+#endif
+
+    if ( dwUsage == 0 )
+    {
+        dwUsage = RESOURCEUSAGE_ALL;
+    }
+
+#if 0
+    if ( dwType == 0 || dwType == RESOURCEUSAGE_ATTACHED)
+    {
+        dwType |= RESOURCETYPE_DISK | RESOURCETYPE_PRINT;
+    }
+#endif
+
+    *lphEnum = HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, sizeof( AFSEnumerationCB));
+
+    if( *lphEnum == NULL)
+    {
+
+        return WN_OUT_OF_MEMORY;
+    }
+
+    pEnumCB = (AFSEnumerationCB *)*lphEnum;
+
+    pEnumCB->CurrentIndex = 0;
+
+    pEnumCB->Type = dwType;
+
+    switch( dwScope )
+    {
+        case RESOURCE_CONNECTED:
+        {
+
+            pEnumCB->Scope = RESOURCE_CONNECTED;
+
+            break;
+        }
+
+        case RESOURCE_CONTEXT:
+        {
+
+            pEnumCB->Scope = RESOURCE_CONTEXT;
+
+            break;
+        }
+
+        case RESOURCE_GLOBALNET:
+        {
+
+            if( lpNetResource != NULL &&
+                lpNetResource->lpRemoteName != NULL)
+            {
+
+                pEnumCB->RemoteName = (WCHAR *)HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, 0x1000);
+
+                if( pEnumCB->RemoteName == NULL)
+                {
+
+                    dwStatus = WN_OUT_OF_MEMORY;
+                    HeapFree( GetProcessHeap( ), 0, (PVOID) *lphEnum );
+                    *lphEnum = NULL;
+                }
+                else
+                {
+
+                    StringCbCopy( pEnumCB->RemoteName,
+                                  0x1000,
+                                  lpNetResource->lpRemoteName);
+
+                }
+            }
+
+            pEnumCB->Scope = RESOURCE_GLOBALNET;
+
+            break;
+        }
+
+        default:
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPOpenEnum Processing (Scope %s 0x%x) Type %s Usage %s, returning WN_NOT_SUPPORTED\n",
+                         GetScopeString(dwScope), dwScope, GetTypeString(dwType), GetUsageString(dwUsage));
+#endif
+
+            dwStatus  = WN_NOT_SUPPORTED;
+            HeapFree( GetProcessHeap( ), 0, (PVOID) *lphEnum );
+            *lphEnum = NULL;
+
+            break;
+    }
+
+    return dwStatus;
+}
+
+
+DWORD
+APIENTRY
+NPEnumResource( HANDLE  hEnum,
+                LPDWORD lpcCount,
+                LPVOID  lpBuffer,
+                LPDWORD lpBufferSize)
+{
+
+    DWORD            dwStatus = WN_NO_MORE_ENTRIES; //WN_SUCCESS;
+    ULONG            dwCopyBytes;
+    ULONG            EntriesCopied;
+    ULONG            EntriesRequested;
+    ULONG            dwIndex;
+    LPNETRESOURCE    pNetResource;
+    ULONG            SpaceNeeded;
+    ULONG            SpaceAvailable;
+    PWCHAR           StringZone;
+    AFSNetworkProviderConnectionCB *pConnectionCB = NULL;
+    void            *pConnectionCBBase = NULL;
+    DWORD            dwError = 0;
+    UNICODE_STRING   uniRemoteName;
+    HANDLE           hControlDevice = NULL;
+    AFSEnumerationCB *pEnumCB = (AFSEnumerationCB *)hEnum;
+
+    __Enter
+    {
+
+        if ( lpBufferSize == NULL)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource No output size, returning WN_BAD_VALUE\n");
+#endif
+            try_return( dwStatus = WN_BAD_VALUE);
+        }
+
+        ReadProviderNameString();
+
+        pNetResource = (LPNETRESOURCE) lpBuffer;
+        SpaceAvailable = *lpBufferSize;
+        EntriesRequested = *lpcCount;
+        *lpcCount = EntriesCopied = 0;
+        StringZone = (PWCHAR) ((char *)lpBuffer + *lpBufferSize);
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPEnumResource Processing Remote name %s Scope %s Type %s Usage %s Index %d SpaceAvailable 0x%lX RequestedEntries %lu\n",
+                     pEnumCB->RemoteName ? pEnumCB->RemoteName : L"(Null)",
+                     GetScopeString(pEnumCB->Scope),
+                     GetTypeString(pEnumCB->Type),
+                     GetUsageString(pEnumCB->Type),
+                     pEnumCB->CurrentIndex,
+                     SpaceAvailable,
+                     EntriesRequested);
+#endif
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource AFSRDFS is disabled, returning WN_NO_MORE_ENTRIES\n");
+#endif
+
+            try_return( dwStatus = WN_NO_MORE_ENTRIES);
+        }
+
+        pConnectionCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
+
+        if( pConnectionCB == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource Out of Memory\n");
+#endif
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectionCBBase = (void *)pConnectionCB;
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        //
+        // Handle the special cases here
+        //   0. Provider Network Root
+        //   1. Server Root
+        //
+
+#if 0
+        if ( pEnumCB->Scope == RESOURCE_GLOBALNET)
+        {
+
+            ReadServerNameString();
+
+            if ( pEnumCB->CurrentIndex == 0 &&
+                 pEnumCB->RemoteName == NULL)
+            {
+
+                // Determine the space needed for this entry...
+
+                SpaceNeeded = 2 * ( cbProviderNameLength + sizeof( WCHAR));
+
+                uniRemoteName.Length = (USHORT)cbProviderNameLength;
+                uniRemoteName.MaximumLength = uniRemoteName.Length;
+                uniRemoteName.Buffer = wszProviderName;
+
+                if( SpaceNeeded + sizeof( NETRESOURCE) > SpaceAvailable)
+                {
+
+                    *lpBufferSize = SpaceNeeded + sizeof( NETRESOURCE);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPEnumResource Request MORE_DATA for entry %s Len %d\n",
+                                 &uniRemoteName,
+                                 *lpBufferSize);
+#endif
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPEnumResource Processing Entry %wZ\n",
+                             &uniRemoteName);
+#endif
+
+                SpaceAvailable -= (SpaceNeeded + sizeof( NETRESOURCE));
+
+                pNetResource->dwScope       = RESOURCE_GLOBALNET;
+                pNetResource->dwType        = RESOURCETYPE_ANY;
+                pNetResource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
+                pNetResource->dwUsage       = RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_RESERVED;
+
+                // setup string area at opposite end of buffer
+                StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+                pNetResource->lpLocalName = NULL;
+
+                // copy remote name
+                pNetResource->lpRemoteName = StringZone;
+
+                StringCbCopy( StringZone,
+                              cbProviderNameLength + sizeof( WCHAR),
+                              wszProviderName);
+
+                StringZone += cbProviderNameLength / sizeof(WCHAR) + 1;
+
+                pNetResource->lpComment = NULL;
+
+                // copy provider name
+                pNetResource->lpProvider = StringZone;
+                StringCbCopy( StringZone,
+                              cbProviderNameLength + sizeof( WCHAR),
+                              wszProviderName);
+
+                StringZone += cbProviderNameLength / sizeof( WCHAR) + 1;
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPEnumResource Entry (0x%p) Scope %s Type %s Display %s Usage %s Local %s Remote \"%s\" Comment \"%s\"\n",
+                             pNetResource,
+                             GetScopeString(pNetResource->dwScope),
+                             GetTypeString(pNetResource->dwType),
+                             GetDisplayString(pNetResource->dwDisplayType),
+                             GetUsageString(pNetResource->dwUsage),
+                             pNetResource->lpLocalName,
+                             pNetResource->lpRemoteName,
+                             pNetResource->lpComment);
+#endif
+
+                // setup the new end of buffer
+                StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+                EntriesCopied++;
+
+                pNetResource++;
+
+                // do not change the index since we did not query the redirector
+                pEnumCB->CurrentIndex = 0;
+
+                // remember that we returned the provider name
+                pEnumCB->RemoteName = (WCHAR *)HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, 0x1000);
+
+                if( pEnumCB->RemoteName == NULL)
+                {
+
+                    try_return( dwStatus = WN_OUT_OF_MEMORY);
+                }
+                else
+                {
+
+                    StringCbCopy( pEnumCB->RemoteName,
+                                   0x1000,
+                                   wszProviderName);
+                }
+            }
+
+            if ( pEnumCB->CurrentIndex == 0 &&
+                 lstrlen( pEnumCB->RemoteName) == cbProviderNameLength / sizeof( WCHAR) &&
+                 _wcsnicmp( pEnumCB->RemoteName, wszProviderName, cbProviderNameLength / sizeof( WCHAR)) == 0 &&
+                 EntriesCopied < EntriesRequested)
+            {
+
+                //
+                // After the network provider entry comes the server entry
+                //
+
+                // Determine the space needed for this entry...
+
+                SpaceNeeded = cbProviderNameLength + cbServerNameUNCLength + cbServerCommentLength + 3 * sizeof( WCHAR);
+
+                uniRemoteName.Length = (USHORT)cbServerNameUNCLength;
+                uniRemoteName.MaximumLength = uniRemoteName.Length;
+                uniRemoteName.Buffer = wszServerNameUNC;
+
+                if( SpaceNeeded + sizeof( NETRESOURCE) > SpaceAvailable)
+                {
+
+                    *lpBufferSize = SpaceNeeded + sizeof( NETRESOURCE);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPEnumResource Request MORE_DATA for entry %s Len %d\n",
+                                 &uniRemoteName,
+                                 *lpBufferSize);
+#endif
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPEnumResource Processing Entry %wZ\n",
+                             &uniRemoteName);
+#endif
+
+                SpaceAvailable -= (SpaceNeeded + sizeof( NETRESOURCE));
+
+                pNetResource->dwScope       = 0;
+                pNetResource->dwType        = RESOURCETYPE_ANY;
+                pNetResource->dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+                pNetResource->dwUsage       = RESOURCEUSAGE_CONTAINER;
+
+                // setup string area at opposite end of buffer
+                StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+                pNetResource->lpLocalName = NULL;
+
+                // copy remote name
+                pNetResource->lpRemoteName = StringZone;
+
+                StringCbCopy( StringZone,
+                              cbServerNameUNCLength + sizeof( WCHAR),
+                              wszServerNameUNC);
+
+                StringZone += cbServerNameUNCLength / sizeof(WCHAR) + 1;
+
+                // copy comment
+                pNetResource->lpComment = StringZone;
+
+                StringCbCopy( StringZone,
+                              cbServerCommentLength + sizeof( WCHAR),
+                              wszServerComment);
+
+                StringZone += cbServerCommentLength / sizeof( WCHAR) + 1;
+
+                // copy provider name
+                pNetResource->lpProvider = StringZone;
+                StringCbCopy( StringZone,
+                              cbProviderNameLength + sizeof( WCHAR),
+                              wszProviderName);
+
+                StringZone += cbProviderNameLength / sizeof( WCHAR) + 1;
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPEnumResource Entry (0x%p) Scope %s Type %s Display %s Usage %s Local %s Remote \"%s\" Comment \"%s\"\n",
+                             pNetResource,
+                             GetScopeString(pNetResource->dwScope),
+                             GetTypeString(pNetResource->dwType),
+                             GetDisplayString(pNetResource->dwDisplayType),
+                             GetUsageString(pNetResource->dwUsage),
+                             pNetResource->lpLocalName,
+                             pNetResource->lpRemoteName,
+                             pNetResource->lpComment);
+#endif
+
+                // setup the new end of buffer
+                StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+                EntriesCopied++;
+
+                pNetResource++;
+
+                // do not update the index because we did not query the redirector
+                pEnumCB->CurrentIndex = 0;
+
+                // remember that we returned the server
+                StringCbCopy( pEnumCB->RemoteName,
+                              0x1000,
+                              wszServerNameUNC);
+            }
+        }
+#endif
+
+        //
+        // Setup what we are going to ask for
+        //
+
+        pConnectionCB->Scope = pEnumCB->Scope;
+
+        pConnectionCB->Type = pEnumCB->Type;
+
+        pConnectionCB->CurrentIndex = pEnumCB->CurrentIndex;
+
+        pConnectionCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        //
+        // If this is a RESOURCE_GLOBALNET enumeration then pass down the remote name if
+        // there is one
+        //
+
+        pConnectionCB->RemoteNameLength = 0;
+
+        if( pEnumCB->Scope == RESOURCE_GLOBALNET &&
+            pEnumCB->RemoteName != NULL)
+        {
+
+            pConnectionCB->RemoteNameLength = wcslen( pEnumCB->RemoteName) * sizeof( WCHAR);
+
+            StringCbCopy( pConnectionCB->RemoteName,
+                          (0x1000 - sizeof(AFSNetworkProviderConnectionCB)) + sizeof(WCHAR),
+                          pEnumCB->RemoteName);
+        }
+
+        pConnectionCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPEnumResource Retrieved authentication id %08lX-%08lX\n",
+                     pConnectionCB->AuthenticationId.HighPart,
+                     pConnectionCB->AuthenticationId.LowPart);
+#endif
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_LIST_CONNECTIONS,
+                                   pConnectionCB,
+                                   0x1000,
+                                   pConnectionCB,
+                                   0x1000,
+                                   &dwCopyBytes,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPEnumResource Failed to list connections from file system - gle 0x%x\n",
+                         gle);
+#endif
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        if( dwCopyBytes == 0)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource No More Entries\n");
+#endif
+            try_return( dwStatus = WN_NO_MORE_ENTRIES);
+        }
+
+        dwIndex = pEnumCB->CurrentIndex;
+
+        while( EntriesCopied < EntriesRequested)
+        {
+
+            uniRemoteName.Length = (USHORT)pConnectionCB->RemoteNameLength;
+            uniRemoteName.MaximumLength = uniRemoteName.Length;
+            uniRemoteName.Buffer = pConnectionCB->RemoteName;
+
+            // Determine the space needed for this entry...
+
+            SpaceNeeded  = 0;
+
+            if( pConnectionCB->LocalName != 0)
+            {
+
+                SpaceNeeded += 3 * sizeof(WCHAR);                // local name
+            }
+
+            SpaceNeeded += pConnectionCB->RemoteNameLength + sizeof( WCHAR);        // remote name
+
+            if( pConnectionCB->CommentLength > 0)
+            {
+
+                SpaceNeeded += pConnectionCB->CommentLength + sizeof( WCHAR);           // comment
+            }
+
+            SpaceNeeded += cbProviderNameLength + sizeof( WCHAR);           // provider name
+
+            if( SpaceNeeded + sizeof( NETRESOURCE) > SpaceAvailable)
+            {
+
+                if (EntriesCopied == 0) {
+
+                    dwStatus = WN_MORE_DATA;
+
+                    *lpBufferSize = SpaceNeeded + sizeof( NETRESOURCE);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPEnumResource Request MORE_DATA for entry %s Len %d\n",
+                                 &uniRemoteName,
+                                 *lpBufferSize);
+#endif
+
+                } else {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPEnumResource Return SUCCESS but more entries Index %d\n",
+                                 dwIndex);
+#endif
+
+                    dwStatus = WN_SUCCESS;
+                }
+
+                break;
+            }
+
+            SpaceAvailable -= (SpaceNeeded + sizeof( NETRESOURCE));
+
+            pNetResource->dwScope       = pConnectionCB->Scope;
+            pNetResource->dwType        = pConnectionCB->Type;
+
+            pNetResource->dwDisplayType = pConnectionCB->DisplayType;
+
+            if ( pNetResource->dwType == RESOURCETYPE_ANY &&
+                 pNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE)
+            {
+
+                pNetResource->dwType = RESOURCETYPE_DISK;
+            }
+            pNetResource->dwUsage       = pConnectionCB->Usage;
+
+            // setup string area at opposite end of buffer
+            StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+            // copy local name
+            if( pConnectionCB->LocalName != 0)
+            {
+
+                pNetResource->lpLocalName = StringZone;
+                *StringZone++ = towupper(pConnectionCB->LocalName);
+                *StringZone++ = L':';
+                *StringZone++ = L'\0';
+            }
+            else
+            {
+
+                pNetResource->lpLocalName = NULL;
+            }
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource Processing Entry %wZ\n",
+                         &uniRemoteName);
+#endif
+
+            // copy remote name
+            pNetResource->lpRemoteName = StringZone;
+
+            CopyMemory( StringZone,
+                        pConnectionCB->RemoteName,
+                        pConnectionCB->RemoteNameLength);
+
+            StringZone += (pConnectionCB->RemoteNameLength / sizeof(WCHAR));
+
+            *StringZone++ = L'\0';
+
+            // copy comment
+            if( pConnectionCB->CommentLength > 0)
+            {
+
+                pNetResource->lpComment = StringZone;
+
+                CopyMemory( StringZone,
+                            (void *)((char *)pConnectionCB + pConnectionCB->CommentOffset),
+                            pConnectionCB->CommentLength);
+
+                StringZone += (pConnectionCB->CommentLength / sizeof(WCHAR));
+
+                *StringZone++ = L'\0';
+            }
+            else
+            {
+
+                pNetResource->lpComment = NULL;
+            }
+
+            // copy provider name
+            pNetResource->lpProvider = StringZone;
+            StringCbCopy( StringZone,
+                          cbProviderNameLength + sizeof( WCHAR),
+                          wszProviderName);
+
+            StringZone += (cbProviderNameLength / sizeof( WCHAR) + 1);
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPEnumResource Entry (0x%p) Scope %s Type %s Display %s Usage %s Local %s Remote \"%s\" Comment \"%s\"\n",
+                         pNetResource,
+                         GetScopeString(pNetResource->dwScope),
+                         GetTypeString(pNetResource->dwType),
+                         GetDisplayString(pNetResource->dwDisplayType),
+                         GetUsageString(pNetResource->dwUsage),
+                         pNetResource->lpLocalName,
+                         pNetResource->lpRemoteName,
+                         pNetResource->lpComment);
+#endif
+
+            // setup the new end of buffer
+            StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded);
+
+            EntriesCopied++;
+
+            pNetResource++;
+
+            dwIndex++;
+
+            dwCopyBytes -= FIELD_OFFSET( AFSNetworkProviderConnectionCB, RemoteName) +
+                           pConnectionCB->RemoteNameLength +
+                           pConnectionCB->CommentLength;
+
+            if( dwCopyBytes == 0)
+            {
+
+                dwStatus = WN_SUCCESS;
+
+                break;
+            }
+
+            pConnectionCB = (AFSNetworkProviderConnectionCB *)((char *)pConnectionCB +
+                            FIELD_OFFSET( AFSNetworkProviderConnectionCB, RemoteName) +
+                            pConnectionCB->RemoteNameLength +
+                            pConnectionCB->CommentLength);
+        }
+
+        *lpcCount = EntriesCopied;
+
+        // update entry index
+        pEnumCB->CurrentIndex = dwIndex;
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPEnumResource Completed Count %d Index %d\n",
+                     EntriesCopied,
+                     dwIndex);
+#endif
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectionCBBase != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectionCBBase);
+        }
+    }
+
+    return dwStatus;
+}
+
+/*++
+
+Routine Description:
+
+    This routine closes the handle for enumeration of resources.
+
+Arguments:
+
+    hEnum  - the enumeration handle
+
+Return Value:
+
+    WN_SUCCESS if successful, otherwise the appropriate error
+
+Notes:
+
+    The sample only supports the notion of enumerating connected shares
+
+--*/
+
+DWORD APIENTRY
+NPCloseEnum( HANDLE hEnum )
+{
+
+    AFSEnumerationCB *pEnumCB = (AFSEnumerationCB *)hEnum;
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPCloseEnum\n");
+#endif
+
+    if( pEnumCB->RemoteName != NULL)
+    {
+
+        HeapFree( GetProcessHeap( ), 0, (PVOID) pEnumCB->RemoteName);
+    }
+
+    HeapFree( GetProcessHeap( ), 0, (PVOID) hEnum );
+
+    return WN_SUCCESS;
+}
+
+DWORD APIENTRY
+NPGetResourceParent( LPNETRESOURCE   lpNetResource,
+                     LPVOID  lpBuffer,
+                     LPDWORD lpBufferSize )
+{
+
+    DWORD    dwStatus = WN_ACCESS_DENIED;
+    WCHAR   *pwchRemoteName = NULL, *pwchSearch = NULL, *pwchSystem = NULL;
+    LPNETRESOURCE lpOutResource = (LPNETRESOURCE) lpBuffer;
+
+    if ( lpNetResource == NULL)
+    {
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceParent NULL NETRESOURCE\n");
+#endif
+        return WN_MORE_DATA;
+    }
+
+    if( lpNetResource->lpRemoteName == NULL)
+    {
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceParent NULL NETRESOURCE\n");
+#endif
+        return WN_BAD_NETNAME;
+    }
+
+    if ( lpNetResource->dwType != 0 &&
+         lpNetResource->dwType != RESOURCETYPE_DISK)
+    {
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceParent Bad dwType\n");
+#endif
+        return WN_BAD_VALUE;
+    }
+
+    if ( lpBufferSize == NULL )
+    {
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceParent Null lpBufferSize\n");
+#endif
+        return WN_BAD_VALUE;
+    }
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPGetResourceParent For remote name %s\n",
+                 lpNetResource->lpRemoteName);
+#endif
+
+    pwchRemoteName = lpNetResource->lpRemoteName;
+
+    pwchSearch = pwchRemoteName + (wcslen( pwchRemoteName) - 1);
+
+    while( pwchSearch != pwchRemoteName)
+    {
+
+        if( *pwchSearch == L'\\')
+        {
+
+            *pwchSearch = L'\0';
+
+            break;
+        }
+
+        pwchSearch--;
+    }
+
+    if( pwchSearch != pwchRemoteName)
+    {
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceParent Processing parent %s\n",
+                     lpNetResource->lpRemoteName);
+#endif
+
+        dwStatus = NPGetResourceInformation( lpNetResource,
+                                             lpBuffer,
+                                             lpBufferSize,
+                                             &pwchSystem);
+    }
+    else
+    {
+        if ( lpOutResource == NULL ||
+             *lpBufferSize < sizeof( NETRESOURCE) )
+        {
+            *lpBufferSize = sizeof( NETRESOURCE);
+
+            return WN_MORE_DATA;
+        }
+
+        memset( lpOutResource, 0, sizeof( NETRESOURCE));
+
+        return WN_SUCCESS;
+
+    }
+
+    return dwStatus;
+}
+
+DWORD APIENTRY
+NPGetResourceInformation( LPNETRESOURCE   lpNetResource,
+                          LPVOID  lpBuffer,
+                          LPDWORD lpBufferSize,
+                          LPWSTR  *lplpSystem )
+{
+
+    DWORD    dwStatus = WN_NOT_CONNECTED;
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    HANDLE   hControlDevice = NULL;
+    NETRESOURCE *pNetResource = (NETRESOURCE *)lpBuffer;
+    PWCHAR   pStringZone = NULL;
+    UNICODE_STRING uniRemoteName;
+    DWORD    ulRequiredLen = 0;
+    DWORD    dwPassedSize;
+
+
+    __Enter
+    {
+        if ( lplpSystem)
+        {
+            *lplpSystem = NULL;
+        }
+
+        ReadProviderNameString();
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetResourceInformation AFSRDFS is disabled, returning WN_BAD_NETNAME\n");
+#endif
+
+            try_return( dwStatus = WN_BAD_NETNAME);
+        }
+
+        if ( lpNetResource == NULL ||
+             lpBufferSize == NULL )
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetResourceInformaton Null lpNetResource or lpBufferSize\n");
+#endif
+            return WN_BAD_VALUE;
+        }
+
+        if( lpNetResource->lpRemoteName == NULL)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetResourceInformation No resource name\n");
+#endif
+
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        dwPassedSize = *lpBufferSize;
+
+        dwBufferSize = 0x1000;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectCB->RemoteNameLength = wcslen( lpNetResource->lpRemoteName) * sizeof( WCHAR);
+
+        StringCbCopy( pConnectCB->RemoteName,
+                      dwBufferSize - sizeof(AFSNetworkProviderConnectionCB),
+                      lpNetResource->lpRemoteName);
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceInformation Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetResourceInformation OpenRedirector failure, returning WN_NET_ERROR\n");
+#endif
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_GET_CONNECTION_INFORMATION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   lpBufferSize,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPGetResourceInformation Failed to get connection info from file system for local %s gle 0x%x\n",
+                         lpNetResource->lpRemoteName, gle);
+#endif
+            try_return( dwStatus = WN_BAD_NETNAME);
+        }
+
+        uniRemoteName.Length = (USHORT)pConnectCB->RemoteNameLength;
+        uniRemoteName.MaximumLength = uniRemoteName.Length;
+        uniRemoteName.Buffer = pConnectCB->RemoteName;
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetResourceInformation For remote name %wZ Scope %08lX Type %08lX Usage %08lX\n",
+                     &uniRemoteName,
+                     pConnectCB->Scope,
+                     pConnectCB->Type,
+                     pConnectCB->Usage);
+#endif
+
+        // Determine the space needed for this entry...
+
+        ulRequiredLen = sizeof( NETRESOURCE);
+
+        ulRequiredLen += pConnectCB->RemoteNameLength + sizeof( WCHAR);
+
+        ulRequiredLen += pConnectCB->CommentLength + sizeof( WCHAR);
+
+        ulRequiredLen += cbProviderNameLength + sizeof( WCHAR);
+
+        ulRequiredLen += pConnectCB->RemainingPathLength + sizeof( WCHAR);
+
+        if( pNetResource == NULL ||
+            ulRequiredLen > dwPassedSize)
+        {
+
+            *lpBufferSize = ulRequiredLen;
+
+            try_return( dwStatus = WN_MORE_DATA);
+        }
+
+        pStringZone = (PWCHAR) ((char *)lpBuffer + sizeof( NETRESOURCE));
+
+        pNetResource->dwScope       = 0 /* pConnectCB->Scope*/;
+        pNetResource->dwType        = 0 /* pConnectCB->Type */;
+
+        pNetResource->dwDisplayType = pConnectCB->DisplayType;
+
+        pNetResource->dwUsage       = pConnectCB->Usage;
+
+        pNetResource->lpLocalName = NULL;
+
+        // copy remote name
+        pNetResource->lpRemoteName = pStringZone;
+
+        CopyMemory( pStringZone,
+                    pConnectCB->RemoteName,
+                    pConnectCB->RemoteNameLength);
+
+        pStringZone += (pConnectCB->RemoteNameLength / sizeof(WCHAR));
+
+        *pStringZone++ = L'\0';
+
+        // copy comment
+        pNetResource->lpComment = pStringZone;
+
+        CopyMemory( pStringZone,
+                    (void *)((char *)pConnectCB + pConnectCB->CommentOffset),
+                    pConnectCB->CommentLength);
+
+        pStringZone += (pConnectCB->CommentLength / sizeof(WCHAR));
+
+        *pStringZone++ = L'\0';
+
+        // copy remaining path
+        if (pConnectCB->RemainingPathLength > 0)
+        {
+            *lplpSystem = pStringZone;
+
+            CopyMemory( pStringZone,
+                        (void *)((char *)pConnectCB + pConnectCB->RemainingPathOffset),
+                        pConnectCB->RemainingPathLength);
+
+            pStringZone += (pConnectCB->RemainingPathLength / sizeof(WCHAR));
+
+            *pStringZone++ = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetResourceInformation For remote name %s returning remaining path %s\n",
+                         pNetResource->lpRemoteName,
+                         *lplpSystem);
+#endif
+        }
+
+        // copy provider name
+        pNetResource->lpProvider = pStringZone;
+
+        StringCbCopy( pStringZone,
+                      cbProviderNameLength + sizeof( WCHAR),
+                      wszProviderName);
+
+        pStringZone += (cbProviderNameLength / sizeof( WCHAR) + 1);
+
+        *lpBufferSize = ulRequiredLen;
+
+        dwStatus = WN_SUCCESS;
+
+try_exit:
+
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwStatus;
+}
+
+static VOID
+SeparateRemainingPath( WCHAR * lpConnectionName, WCHAR **lppRemainingPath)
+{
+    WCHAR *pwch;
+    WCHAR wch1, wch2;
+    DWORD  dwCount;
+
+    //
+    // at this point the lpConnectionName contains the full name.  We need to
+    // truncate it to \\server\share and move the remaining path back one position.
+    //
+
+    for ( pwch = lpConnectionName, dwCount = 0; *pwch; pwch++)
+    {
+        if ( *pwch == L'\\')
+        {
+            dwCount++;
+        }
+
+        if ( dwCount == 4)
+        {
+            break;
+        }
+    }
+
+    if (*pwch == L'\\')
+    {
+        //
+        // Found the remaining path that must be moved
+        //
+
+        *lppRemainingPath = pwch + 1;
+
+        *pwch++ = 0;
+
+        //
+        // Find the end
+        //
+        for ( ; *pwch; pwch++);
+
+        //
+        // and work backwards moving the string
+        // and then make sure that there is at least
+        // a path separator.
+        //
+
+        *(pwch + 1) = 0;
+
+        for ( ;pwch > *lppRemainingPath; pwch--)
+        {
+            *pwch = *(pwch - 1);
+        }
+
+        *pwch = L'\\';
+    }
+}
+
+DWORD APIENTRY
+NPGetUniversalName( LPCWSTR lpLocalPath,
+                    DWORD   dwInfoLevel,
+                    LPVOID  lpBuffer,
+                    LPDWORD lpBufferSize )
+{
+    DWORD    dwStatus = WN_NOT_CONNECTED;
+    WCHAR    wchLocalName[3];
+    WCHAR    wchSubstName[MAX_PATH + 1];
+    AFSNetworkProviderConnectionCB   *pConnectCB = NULL;
+    DWORD    dwError = 0;
+    DWORD    dwBufferSize = 0;
+    DWORD    dwPassedSize = *lpBufferSize;
+    DWORD    dwRemainingLength = *lpBufferSize;
+    HANDLE   hControlDevice = NULL;
+    DWORD    dwLocalPathLength = 0;
+    DWORD    dwRemainingPathLength = 0;
+    CHAR    *pch;
+
+    __Enter
+    {
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetUniversalName local path %s level 0x%X\n",
+                     lpLocalPath ? lpLocalPath : L"(Null)",
+                     dwInfoLevel);
+#endif
+
+        if ( NPIsFSDisabled())
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetUniversalName AFSRDFS is disabled, returning WN_NOT_CONNECTED\n");
+#endif
+
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        dwLocalPathLength = lstrlen( lpLocalPath);
+
+        dwRemainingPathLength = dwLocalPathLength - 2;          // no drive letter
+
+        if( dwLocalPathLength == 0)
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetUniversalName AFSRDFS is disabled, returning WN_BAD_LOCALNAME\n");
+#endif
+
+            try_return( dwStatus = WN_BAD_LOCALNAME);
+        }
+
+        if( lpBuffer == NULL ||
+            lpBufferSize == NULL)
+        {
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetUniversalName No output buffer or size\n");
+#endif
+            try_return( dwStatus = WN_BAD_VALUE);
+        }
+
+        memset(lpBuffer, 0, dwPassedSize);
+
+        if ( !DriveSubstitution( lpLocalPath, wchSubstName, sizeof( wchSubstName)))
+        {
+            wchLocalName[0] = towupper(lpLocalPath[0]);
+            wchLocalName[1] = L':';
+            wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"NPGetUniversalName Requesting UNC for %s level 0x%X\n",
+                         wchLocalName,
+                         dwInfoLevel);
+#endif
+        }
+        else
+        {
+
+            ReadServerNameString();
+
+            if ( wchSubstName[0] != L'\\' &&
+                 wchSubstName[1] == L':')
+            {
+
+                wchLocalName[0] = towupper(wchSubstName[0]);
+                wchLocalName[1] = L':';
+                wchLocalName[2] = L'\0';
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName Requesting UNC for drive substitution %s -> %s\n",
+                             wchSubstName,
+                             wchLocalName);
+#endif
+            }
+            else if ( _wcsnicmp( wchSubstName, wszServerNameUNC, cbServerNameUNCLength / sizeof( WCHAR)) == 0 &&
+                      ( wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == L'\\' ||
+                        wchSubstName[cbServerNameUNCLength / sizeof( WCHAR)] == 0))
+            {
+                HRESULT hr;
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName drive substitution %s is AFS; Level 0x%x BufferSize 0x%x\n",
+                             wchSubstName,
+                             dwInfoLevel,
+                             dwPassedSize);
+#endif
+
+                dwBufferSize = (wcslen( wchSubstName) + 1) * sizeof( WCHAR);
+
+                switch( dwInfoLevel)
+                {
+
+                case UNIVERSAL_NAME_INFO_LEVEL:
+                {
+
+                    UNIVERSAL_NAME_INFO *pUniversalInfo = (UNIVERSAL_NAME_INFO *)lpBuffer;
+
+                    *lpBufferSize = sizeof( UNIVERSAL_NAME_INFO) + dwBufferSize;
+
+                    if( dwPassedSize <= sizeof( UNIVERSAL_NAME_INFO))
+                    {
+
+#ifdef AFS_DEBUG_TRACE
+                        AFSDbgPrint( L"NPGetUniversalName (UNIVERSAL_NAME_INFO) WN_MORE_DATA\n");
+#endif
+
+                        try_return( dwStatus = WN_MORE_DATA);
+                    }
+
+                    dwRemainingLength -= sizeof( UNIVERSAL_NAME_INFO);
+
+                    pUniversalInfo->lpUniversalName = (LPTSTR)((char *)lpBuffer + sizeof( UNIVERSAL_NAME_INFO));
+
+                    memcpy( pUniversalInfo->lpUniversalName,
+                            wchSubstName,
+                            min( dwBufferSize, dwRemainingLength));
+
+                    dwRemainingLength -= min( dwBufferSize, dwRemainingLength);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (UNIVERSAL_NAME_INFO_LEVEL) lpBuffer: %p Name: (%p) \"%s\"\n",
+                                 lpBuffer,
+                                 pUniversalInfo->lpUniversalName,
+                                 pUniversalInfo->lpUniversalName);
+#endif
+
+                    if ( dwPassedSize < *lpBufferSize)
+                    {
+
+                        try_return( dwStatus = WN_MORE_DATA);
+                    }
+
+                    try_return( dwStatus = WN_SUCCESS);
+                }
+
+                case REMOTE_NAME_INFO_LEVEL:
+                {
+
+                    REMOTE_NAME_INFO *pRemoteInfo = (REMOTE_NAME_INFO *)lpBuffer;
+
+                    *lpBufferSize = sizeof( REMOTE_NAME_INFO) + 2 * dwBufferSize + sizeof( WCHAR);
+
+                    if( dwPassedSize <= sizeof( REMOTE_NAME_INFO))
+                    {
+
+#ifdef AFS_DEBUG_TRACE
+                        AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO) WN_MORE_DATA\n");
+#endif
+
+                        try_return( dwStatus = WN_MORE_DATA);
+                    }
+
+                    dwRemainingLength -= sizeof( REMOTE_NAME_INFO);
+
+                    pRemoteInfo->lpUniversalName = (LPTSTR)((char *)lpBuffer + sizeof( REMOTE_NAME_INFO));
+
+                    memcpy( pRemoteInfo->lpUniversalName,
+                            wchSubstName,
+                            min( dwRemainingLength, dwBufferSize));
+
+                    dwRemainingLength -= min( dwRemainingLength, dwBufferSize);
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) UNI lpBuffer: %p Name: (%p) \"%s\"\n",
+                                 lpBuffer,
+                                 pRemoteInfo->lpUniversalName,
+                                 pRemoteInfo->lpUniversalName);
+#endif
+
+                    if ( dwRemainingLength > dwBufferSize + sizeof( WCHAR))
+                    {
+                        pRemoteInfo->lpConnectionName = (LPTSTR)((char *)pRemoteInfo->lpUniversalName + dwBufferSize);
+
+                        memcpy( pRemoteInfo->lpConnectionName,
+                                wchSubstName,
+                                min( dwRemainingLength, dwBufferSize));
+
+                        dwRemainingLength -= min( dwRemainingLength, dwBufferSize) - sizeof( WCHAR);
+
+                        SeparateRemainingPath( pRemoteInfo->lpConnectionName,
+                                               &pRemoteInfo->lpRemainingPath);
+                    }
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) CONN lpBuffer: %p Name: (%p) \"%s\"\n",
+                                 lpBuffer,
+                                 pRemoteInfo->lpConnectionName,
+                                 pRemoteInfo->lpConnectionName ? pRemoteInfo->lpConnectionName : L"(null)");
+
+                    AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) REMAIN lpBuffer: %p Name: (%p) \"%s\"\n",
+                                 lpBuffer,
+                                 pRemoteInfo->lpRemainingPath,
+                                 pRemoteInfo->lpRemainingPath ? pRemoteInfo->lpRemainingPath : L"(null)");
+#endif
+
+                    if ( dwPassedSize < *lpBufferSize)
+                    {
+
+                        try_return( dwStatus = WN_MORE_DATA);
+                    }
+
+                    try_return( dwStatus = WN_SUCCESS);
+                }
+
+                default:
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (UNKNOWN: 0x%X) WN_BAD_VALUE\n",
+                                 dwInfoLevel);
+#endif
+                    try_return( dwStatus = WN_BAD_VALUE);
+                }
+            }
+            else
+            {
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName drive substitution %s is not AFS\n",
+                             wchSubstName);
+#endif
+                try_return( dwStatus = WN_NOT_CONNECTED);
+            }
+        }
+
+        dwBufferSize = 0x1000;
+
+        pConnectCB = (AFSNetworkProviderConnectionCB *)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize);
+
+        if( pConnectCB == NULL)
+        {
+            try_return( dwStatus = WN_OUT_OF_MEMORY);
+        }
+
+        pConnectCB->LocalName = towupper(wchLocalName[0]);
+
+        pConnectCB->RemoteNameLength = 0;
+
+        pConnectCB->Version = AFS_NETWORKPROVIDER_INTERFACE_VERSION_1;
+
+        pConnectCB->AuthenticationId = AFSRetrieveAuthId();
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetUniversalName Retrieved authentication id %08lX-%08lX\n",
+                     pConnectCB->AuthenticationId.HighPart,
+                     pConnectCB->AuthenticationId.LowPart);
+#endif
+
+        hControlDevice = OpenRedirector();
+
+        if( hControlDevice == NULL)
+        {
+
+            try_return( dwStatus = WN_NET_ERROR);
+        }
+
+        dwError = DeviceIoControl( hControlDevice,
+                                   IOCTL_AFS_GET_CONNECTION,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   pConnectCB,
+                                   dwBufferSize,
+                                   &dwBufferSize,
+                                   NULL);
+
+        if( !dwError)
+        {
+#ifdef AFS_DEBUG_TRACE
+            DWORD gle = GetLastError();
+
+            AFSDbgPrint( L"NPGetUniversalName Failed to get connection from file system for local %s gle 0x%x\n",
+                         wchLocalName, gle);
+#endif
+            try_return( dwStatus = WN_NOT_CONNECTED);
+        }
+
+        switch( dwInfoLevel)
+        {
+
+            case UNIVERSAL_NAME_INFO_LEVEL:
+            {
+
+                UNIVERSAL_NAME_INFO *pUniversalInfo = (UNIVERSAL_NAME_INFO *)lpBuffer;
+
+                *lpBufferSize = sizeof( UNIVERSAL_NAME_INFO) + dwBufferSize + sizeof( WCHAR);
+
+                *lpBufferSize += dwRemainingPathLength * sizeof( WCHAR);
+
+                if( dwPassedSize <= sizeof( UNIVERSAL_NAME_INFO))
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (UNIVERSAL_NAME_INFO) WN_MORE_DATA\n");
+#endif
+
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+                dwRemainingLength -= sizeof( UNIVERSAL_NAME_INFO);
+
+                pUniversalInfo->lpUniversalName = (LPTSTR)((char *)lpBuffer + sizeof( UNIVERSAL_NAME_INFO));
+
+                pch = (char *)pUniversalInfo->lpUniversalName;
+
+                memcpy( pch,
+                        pConnectCB,
+                        min( dwBufferSize, dwRemainingLength));
+
+                pch += min( dwBufferSize, dwRemainingLength);
+
+                dwRemainingLength -= min( dwBufferSize + sizeof(WCHAR), dwRemainingLength);
+
+                memcpy( pch,
+                        &lpLocalPath[2],
+                        min(dwRemainingPathLength * sizeof( WCHAR), dwRemainingLength));
+
+                pch += min(dwRemainingPathLength * sizeof( WCHAR), dwRemainingLength);
+
+                dwRemainingLength -= min(dwRemainingPathLength * sizeof( WCHAR), dwRemainingLength);
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName (UNIVERSAL_NAME_INFO_LEVEL) lpBuffer: %p Name: (%p) \"%s\"\n",
+                             lpBuffer,
+                             pUniversalInfo->lpUniversalName,
+                             pUniversalInfo->lpUniversalName);
+#endif
+
+                if ( dwPassedSize < *lpBufferSize)
+                {
+
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+                try_return( dwStatus = WN_SUCCESS);
+            }
+
+            case REMOTE_NAME_INFO_LEVEL:
+            {
+
+                REMOTE_NAME_INFO *pRemoteInfo = (REMOTE_NAME_INFO *)lpBuffer;
+
+                *lpBufferSize = sizeof( REMOTE_NAME_INFO) + (2 * dwBufferSize + sizeof( WCHAR)) + 2 * sizeof( WCHAR);
+
+                *lpBufferSize += 2 * dwRemainingPathLength * sizeof( WCHAR);
+
+                if( dwPassedSize <= sizeof( REMOTE_NAME_INFO))
+                {
+
+#ifdef AFS_DEBUG_TRACE
+                    AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO) WN_MORE_DATA\n");
+#endif
+
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+                dwRemainingLength -= sizeof( REMOTE_NAME_INFO);
+
+                pRemoteInfo->lpUniversalName = (LPTSTR)((char *)lpBuffer + sizeof( REMOTE_NAME_INFO));
+
+                pch = (char *)pRemoteInfo->lpUniversalName;
+
+                memcpy( pch,
+                        pConnectCB,
+                        min( dwBufferSize, dwRemainingLength));
+
+                pch += min( dwBufferSize, dwRemainingLength);
+
+                dwRemainingLength -= min( dwBufferSize + sizeof( WCHAR), dwRemainingLength);
+
+                memcpy( pch,
+                        &lpLocalPath[2],
+                        min(dwRemainingPathLength * sizeof( WCHAR), dwRemainingLength));
+
+                pch += min((dwRemainingPathLength + 1) * sizeof( WCHAR), dwRemainingLength);
+
+                dwRemainingLength -= min((dwRemainingPathLength + 1) * sizeof( WCHAR), dwRemainingLength);
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) UNI lpBuffer: %p Name: (%p) \"%s\"\n",
+                             lpBuffer,
+                             pRemoteInfo->lpUniversalName,
+                             pRemoteInfo->lpUniversalName);
+#endif
+
+                if ( dwRemainingLength > dwBufferSize + sizeof( WCHAR))
+                {
+                    pRemoteInfo->lpConnectionName = (LPWSTR)pch;
+
+                    memcpy( pch,
+                            pConnectCB,
+                            min( dwBufferSize, dwRemainingLength));
+
+                    pch += min( dwBufferSize + sizeof( WCHAR), dwRemainingLength);
+
+                    dwRemainingLength -= min( dwBufferSize + sizeof( WCHAR), dwRemainingLength);
+                }
+
+
+                if ( dwRemainingLength > dwRemainingPathLength + sizeof( WCHAR))
+                {
+                    pRemoteInfo->lpRemainingPath = (LPWSTR)pch;
+
+                    memcpy( pch,
+                            &lpLocalPath[2],
+                            min((dwRemainingPathLength + 1) * sizeof( WCHAR), dwRemainingLength));
+
+                    pch += min((dwRemainingPathLength + 1) * sizeof( WCHAR), dwRemainingLength);
+
+                    dwRemainingLength -= min((dwLocalPathLength + 1) * sizeof( WCHAR), dwRemainingLength);
+                }
+
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) CONN lpBuffer: %p Name: (%p) \"%s\"\n",
+                             lpBuffer,
+                             pRemoteInfo->lpConnectionName,
+                             pRemoteInfo->lpConnectionName ? pRemoteInfo->lpConnectionName : L"(null)");
+
+                AFSDbgPrint( L"NPGetUniversalName (REMOTE_NAME_INFO_LEVEL) REMAIN lpBuffer: %p Name: (%p) \"%s\"\n",
+                             lpBuffer,
+                             pRemoteInfo->lpRemainingPath,
+                             pRemoteInfo->lpRemainingPath ? pRemoteInfo->lpRemainingPath : L"(null)");
+#endif
+
+                if ( dwPassedSize < *lpBufferSize)
+                {
+
+                    try_return( dwStatus = WN_MORE_DATA);
+                }
+
+                try_return( dwStatus = WN_SUCCESS);
+            }
+
+            default:
+#ifdef AFS_DEBUG_TRACE
+                AFSDbgPrint( L"NPGetUniversalName (UNKNOWN: 0x%X) WN_BAD_VALUE\n",
+                             dwInfoLevel);
+#endif
+                try_return( dwStatus = WN_BAD_VALUE);
+        }
+
+try_exit:
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPGetUniversalName BufferSize 0x%X\n",
+                     *lpBufferSize);
+#endif
+        if ( hControlDevice != NULL)
+        {
+
+            CloseHandle( hControlDevice);
+        }
+
+        if( pConnectCB != NULL)
+        {
+
+            HeapFree( GetProcessHeap( ), 0, (PVOID) pConnectCB);
+        }
+    }
+
+    return dwStatus;
+}
+
+
+static LPCWSTR
+GetFormatFlags( DWORD dwFlags)
+{
+    static WCHAR Buffer[128] = L"";
+
+    //
+    // WNFMT_MULTILINE         0x01
+    // WNFMT_ABBREVIATED       0x02
+    // WNFMT_INENUM            0x10
+    // WNFMT_CONNECTION        0x20
+    //
+
+    Buffer[0] = L'\0';
+
+    if ( dwFlags & WNFMT_MULTILINE )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"MULTILINE|");
+    }
+
+    if ( dwFlags & WNFMT_INENUM )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"ABBREVIATED|");
+    }
+
+    if ( dwFlags & WNFMT_INENUM )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"INENUM|");
+    }
+
+    if ( dwFlags & WNFMT_CONNECTION )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"CONNECTION|");
+    }
+
+    if ( dwFlags & ~(WNFMT_MULTILINE|WNFMT_ABBREVIATED|WNFMT_INENUM|WNFMT_CONNECTION) )
+    {
+        StringCbCat( Buffer, sizeof(Buffer), L"UNKNOWN|");
+    }
+
+    Buffer[lstrlen(Buffer)-1] = L'\0';
+
+    return Buffer;
+}
+
+DWORD
+NPFormatNetworkName( LPTSTR  lpRemoteName,
+                     LPTSTR  lpFormattedName,
+                     LPDWORD lpnLength,
+                     DWORD dwFlags,
+                     DWORD dwAveCharPerLine)
+{
+
+    DWORD dwLen = 0, dwCurrentLen = 0;
+    LPTSTR pCurrentName = lpRemoteName;
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPFormatNetworkName Remote %s Flags %s (0x%x) CharsPerLine %u\n",
+                 lpRemoteName,
+                 dwFlags,
+                 GetFormatFlags( dwFlags),
+                 dwAveCharPerLine);
+#endif
+
+
+    //
+    // Walk back in the name until we hit a \
+    //
+
+    dwLen = wcslen( lpRemoteName);
+
+    pCurrentName += (dwLen - 1);
+
+    if ( pCurrentName[ 0] != L'\\')
+    {
+
+        while( dwLen > 0)
+        {
+
+            if( pCurrentName[ 0] == L'\\')
+            {
+
+                pCurrentName++;
+
+                break;
+            }
+
+            pCurrentName--;
+
+            dwLen--;
+
+            dwCurrentLen++;
+        }
+    }
+
+    if( *lpnLength  < dwCurrentLen * sizeof( WCHAR))
+    {
+
+        *lpnLength = dwCurrentLen * sizeof( WCHAR);
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"NPFormatNetworkName remote name %s WN_MORE_DATA\n",
+                     lpRemoteName);
+#endif
+
+        return WN_MORE_DATA;
+    }
+
+    StringCbCopy( lpFormattedName,
+                  *lpnLength,
+                  pCurrentName);
+
+    *lpnLength = dwCurrentLen * sizeof( WCHAR);
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPFormatNetworkName remote name %s as %s\n",
+                 lpRemoteName,
+                 lpFormattedName);
+#endif
+
+    return WN_SUCCESS;
+}
+
+/************************************************************
+/       Unsupported entry points
+/************************************************************/
+
+//
+// AuthGroup processing is implemented in src/WINNT/afsd/afslogon.c
+//
+DWORD APIENTRY
+NPLogonNotify(
+    PLUID   lpLogonId,
+    LPCWSTR lpAuthentInfoType,
+    LPVOID  lpAuthentInfo,
+    LPCWSTR lpPreviousAuthentInfoType,
+    LPVOID  lpPreviousAuthentInfo,
+    LPWSTR  lpStationName,
+    LPVOID  StationHandle,
+    LPWSTR  *lpLogonScript)
+{
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPLogonNotify, returning WN_NOT_SUPPORTED\n");
+#endif
+
+    return WN_NOT_SUPPORTED;
+}
+
+DWORD APIENTRY
+NPPasswordChangeNotify (
+    LPCWSTR    lpAuthentInfoType,
+    LPVOID    lpAuthentInfo,
+    LPCWSTR    lpPreviousAuthentInfoType,
+    LPVOID    lpPreviousAuthentInfo,
+    LPWSTR    lpStationName,
+    LPVOID    StationHandle,
+    DWORD    dwChangeInfo )
+{
+
+#ifdef AFS_DEBUG_TRACE
+    AFSDbgPrint( L"NPPasswordChangeNotify, returning WN_NOT_SUPPORTED\n");
+#endif
+
+    SetLastError( WN_NOT_SUPPORTED );
+
+    return WN_NOT_SUPPORTED;
+}
+
+DWORD APIENTRY
+NPGetUser( LPTSTR lpName,
+           LPTSTR lpUserName,
+           LPDWORD lpBufferSize)
+{
+
+    DWORD rc = WN_NOT_SUPPORTED;
+
+    AFSDbgPrint( L"NPGetUser Entry Name %s\n", lpName);
+
+    return rc;
+}
+
+
+DWORD
+APIENTRY
+NPGetReconnectFlags( LPWSTR  lpRemoteName,
+                     unsigned char *Parameter2)
+{
+
+    DWORD    dwStatus = WN_NOT_SUPPORTED;
+
+    AFSDbgPrint( L"NPGetReconnectFlags RemoteName %s\n",
+                 lpRemoteName);
+
+    return dwStatus;
+}
+
+
+DWORD
+APIENTRY
+I_SystemFocusDialog( VOID)
+{
+
+    DWORD    dwStatus = WN_NOT_SUPPORTED;
+
+    AFSDbgPrint( L"I_SystemFocusDialog\n");
+
+    return dwStatus;
+}
+
+/************************************************************
+/       END Unsupported entry points
+/************************************************************/
+
+
+HANDLE
+OpenRedirector()
+{
+
+    HANDLE hControlDevice = NULL;
+    WCHAR wchError[ 256];
+
+    hControlDevice = CreateFile( AFS_SYMLINK_W,
+                                 GENERIC_READ | GENERIC_WRITE,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                 NULL,
+                                 OPEN_EXISTING,
+                                 0,
+                                 NULL );
+
+    if( hControlDevice == INVALID_HANDLE_VALUE)
+    {
+
+        hControlDevice = NULL;
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"Failed to open control device error: %d\n",
+                     GetLastError());
+#endif
+    }
+#if 0
+    //
+    // only do this if you want network shares to fail to mount
+    // when the file system is not yet ready
+    //
+    else {
+
+        AFSDriverStatusRespCB   respCB;
+        DWORD                   dwBytes;
+
+        memset( &respCB, '\0', sizeof( AFSDriverStatusRespCB));
+
+        if ( !DeviceIoControl( hControlDevice,
+                               IOCTL_AFS_STATUS_REQUEST,
+                               NULL,
+                               0,
+                               (void *)&respCB,
+                               sizeof( AFSDriverStatusRespCB),
+                               &dwBytes,
+                               NULL) ||
+             dwBytes != sizeof(AFSDriverStatusRespCB) ||
+             respCB.Status != AFS_DRIVER_STATUS_READY )
+        {
+
+            CloseHandle( hControlDevice);
+
+            hControlDevice = NULL;
+        }
+    }
+#endif
+
+    return hControlDevice;
+}
+
+LARGE_INTEGER
+AFSRetrieveAuthId()
+{
+
+    LARGE_INTEGER liAuthId = {0,0};
+    HANDLE hToken = NULL;
+    TOKEN_STATISTICS stTokenInfo;
+    DWORD dwCopyBytes = 0;
+
+    if ( !OpenThreadToken( GetCurrentThread(),
+                           TOKEN_QUERY,
+                           FALSE,       // Impersonation
+                           &hToken))
+    {
+        if( !OpenProcessToken( GetCurrentProcess(),
+                               TOKEN_QUERY,
+                               &hToken))
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"AFSRetrieveAuthId Failed to retrieve Thread and Process tokens 0x%X\n",
+                         GetLastError());
+#endif
+        }
+        else
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"AFSRetrieveAuthId Retrieved Process Token\n");
+#endif
+        }
+    }
+    else
+    {
+
+#ifdef AFS_DEBUG_TRACE
+        AFSDbgPrint( L"AFSRetrieveAuthId Retrieved Thread Token\n");
+#endif
+    }
+
+    if ( hToken != NULL)
+    {
+
+        if( !GetTokenInformation( hToken,
+                                  TokenStatistics,
+                                  &stTokenInfo,
+                                  sizeof( TOKEN_STATISTICS),
+                                  &dwCopyBytes))
+        {
+
+#ifdef AFS_DEBUG_TRACE
+            AFSDbgPrint( L"AFSRetrieveAuthId Failed to retrieve token information 0x%X\n",
+                         GetLastError());
+#endif
+        }
+        else
+        {
+
+            liAuthId.HighPart = stTokenInfo.AuthenticationId.HighPart;
+            liAuthId.LowPart = stTokenInfo.AuthenticationId.LowPart;
+        }
+
+        CloseHandle( hToken);
+    }
+
+    return liAuthId;
+}
+
+static BOOL
+Debug(void)
+{
+    static int init = 0;
+    static BOOL debug = 0;
+
+    if ( !init ) {
+        HKEY hk;
+
+        if (RegOpenKey (HKEY_LOCAL_MACHINE,
+                         TEXT("SYSTEM\\CurrentControlSet\\Services\\AFSRedirector\\NetworkProvider"), &hk) == 0)
+        {
+            DWORD dwSize = sizeof(BOOL);
+            DWORD dwType = REG_DWORD;
+            RegQueryValueEx (hk, TEXT("Debug"), NULL, &dwType, (PBYTE)&debug, &dwSize);
+            RegCloseKey (hk);
+        }
+        init = 1;
+    }
+
+    return debug;
+}
+
+ULONG
+_cdecl
+AFSDbgPrint(
+    PWCHAR Format,
+    ...
+    )
+{
+    HRESULT rc = S_OK;
+    WCHAR wszbuffer[512];
+    va_list marker;
+
+    if ( !Debug() )
+        return 0;
+
+    va_start( marker, Format );
+    {
+        StringCbPrintf( wszbuffer, sizeof(wszbuffer), L"[%d-%08X] ",
+#ifdef AMD64
+                           64,
+#else
+                           32,
+#endif
+                           GetCurrentThreadId());
+
+        rc = StringCbVPrintfW( &wszbuffer[ 14], sizeof(wszbuffer) - 14, Format, marker);
+
+        if (SUCCEEDED(rc))
+            OutputDebugString( wszbuffer );
+        else
+            OutputDebugString(L"AFSDbgPrint Failed to create string\n");
+    }
+    return SUCCEEDED(rc) ? 1 : 0;
+}
diff --git a/src/WINNT/afsrdr/npdll/AFS_Npdll.h b/src/WINNT/afsrdr/npdll/AFS_Npdll.h
new file mode 100644 (file)
index 0000000..4e4cbb5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2008 Kernel Drivers, LLC.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice,
+ *   this list of conditions and the following disclaimer in the
+ *   documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of Kernel Drivers, LLC nor the names of its
+ *   contributors may be
+ *   used to endorse or promote products derived from this software without
+ *   specific prior written permission from Kernel Drivers, LLC.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+void ReadProviderNameString( void);
diff --git a/src/WINNT/afsrdr/npdll/AFS_Npdll.rc b/src/WINNT/afsrdr/npdll/AFS_Npdll.rc
new file mode 100644 (file)
index 0000000..77a0144
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
+ * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice,
+ *   this list of conditions and the following disclaimer in the
+ *   documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
+ *   nor the names of their contributors may be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission from Kernel Drivers, LLC and Your File System, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Define VERSIONINFO resource */
+
+#define  AFS_VERINFO_FILE_DESCRIPTION "AFS Redirector Network Provider"
+#define AFS_VERINFO_NAME "AFSRDFSProvider"
+#define AFS_VERINFO_FILENAME "AFSRDFSProvider.dll"
+
+#include "..\AFS_component_version_number.h"
+#include "..\..\..\config\NTVersioninfo.rc"
diff --git a/src/WINNT/afsrdr/npdll/AFS_NpdllMain.C b/src/WINNT/afsrdr/npdll/AFS_NpdllMain.C
new file mode 100644 (file)
index 0000000..ef1d6b9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
+ * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice,
+ *   this list of conditions and the following disclaimer in the
+ *   documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
+ *   nor the names of their contributors may be used to endorse or promote
+ *   products derived from this software without specific prior written
+ *   permission from Kernel Drivers, LLC and Your File System, Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#define _DECL_DLLMAIN
+#include <process.h>
+#include "AFS_Npdll.h"
+
+// NOTE:
+//
+// Function:   DllMain
+//
+// Return:     TRUE  => Success
+//             FALSE => Failure
+
+BOOL
+WINAPI
+DllMain( HINSTANCE hDLLInst,
+         DWORD fdwReason,
+         LPVOID lpvReserved)
+{
+    BOOL       bStatus = TRUE;
+    WORD       wVersionRequested;
+
+    switch( fdwReason)
+    {
+
+    case DLL_PROCESS_ATTACH:
+        ReadProviderNameString();
+        break;
+
+    case DLL_PROCESS_DETACH:
+        break;
+
+    case DLL_THREAD_ATTACH:
+        break;
+
+    case DLL_THREAD_DETACH:
+        break;
+
+    default:
+        break;
+    }
+
+    return( bStatus);
+}
diff --git a/src/WINNT/afsrdr/npdll/SOURCES b/src/WINNT/afsrdr/npdll/SOURCES
new file mode 100644 (file)
index 0000000..0baa110
--- /dev/null
@@ -0,0 +1,31 @@
+
+TARGETNAME=AFSRDFSProvider
+TARGETPATH=..\build
+TARGETTYPE=DYNLINK
+
+TARGETLIBS=$(DDK_LIB_PATH)\user32.lib \
+           $(DDK_LIB_PATH)\kernel32.lib \
+           $(DDK_LIB_PATH)\Advapi32.lib
+
+
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+
+DLLBASE=0x1010000
+
+USE_NTDLL=0
+
+!IF $(FREEBUILD)
+C_DEFINES=$(C_DEFINES)
+!endif
+
+INCLUDES=$(IFSKIT_INC_PATH);..\Common;
+
+SOURCES= AFS_NpdllMain.c \
+         AFS_Npdll.c \
+         AFS_Npdll.rc
+
+UMTYPE=console
+OPTIONAL_NTTEST=
+
+DLLDEF=AFS_Npdll.def
diff --git a/src/WINNT/afsrdr/npdll/tests/enumresources.c b/src/WINNT/afsrdr/npdll/tests/enumresources.c
new file mode 100644 (file)
index 0000000..06036e8
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * This test code was obtained from
+ * http://msdn.microsoft.com/en-us/library/aa385341(VS.85).aspx
+ *
+ * No license specified.
+ */
+
+#pragma comment(lib, "mpr.lib")
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+#include <stdio.h>
+#include <winnetwk.h>
+
+BOOL WINAPI EnumerateFunc(DWORD dwScope, LPNETRESOURCE lpnr);
+void DisplayStruct(int i, LPNETRESOURCE lpnrLocal);
+BOOL WINAPI NetErrorHandler(HWND hwnd, DWORD dwErrorCode, LPSTR lpszFunction);
+
+int main()
+{
+
+    LPNETRESOURCE lpnr = NULL;
+
+    printf("Connected Resources\n");
+    printf("-------------------\n");
+    if (EnumerateFunc(RESOURCE_CONNECTED, lpnr) == FALSE) {
+        printf("Call to EnumerateFunc(CONNECTED) failed\n");
+        return 1;
+    }
+
+    printf("\n");
+    printf("Context Resources\n");
+    printf("-----------------\n");
+    if (EnumerateFunc(RESOURCE_CONTEXT, lpnr) == FALSE) {
+        printf("Call to EnumerateFunc(CONTEXT) failed\n");
+        return 1;
+    }
+
+    printf("\n");
+    printf("Global Resources\n");
+    printf("----------------\n");
+    if (EnumerateFunc(RESOURCE_GLOBALNET, lpnr) == FALSE) {
+        printf("Call to EnumerateFunc(GLOBALNET) failed\n");
+        return 1;
+    }
+
+    printf("\n");
+    printf("Remembered Resources\n");
+    printf("--------------------\n");
+    if (EnumerateFunc(RESOURCE_REMEMBERED, lpnr) == FALSE) {
+        printf("Call to EnumerateFunc(REMEMBERED) failed\n");
+        return 1;
+    }
+
+    return 0;
+}
+
+BOOL WINAPI EnumerateFunc(DWORD dwScope, LPNETRESOURCE lpnr)
+{
+    DWORD dwResult, dwResultEnum;
+    HANDLE hEnum;
+    DWORD cbBuffer = 16384;     // 16K is a good size
+    DWORD cEntries = -1;        // enumerate all possible entries
+    LPNETRESOURCE lpnrLocal;    // pointer to enumerated structures
+    DWORD i;
+    //
+    // Call the WNetOpenEnum function to begin the enumeration.
+    //
+    dwResult = WNetOpenEnum(dwScope, // all network resources
+                            RESOURCETYPE_DISK,   // all resources
+                            RESOURCEUSAGE_ALL,  // enumerate all resources
+                            lpnr,       // NULL first time the function is called
+                            &hEnum);    // handle to the resource
+
+    if (dwResult != NO_ERROR) {
+        printf("WnetOpenEnum failed with error %d\n", dwResult);
+        return FALSE;
+    }
+    //
+    // Call the GlobalAlloc function to allocate resources.
+    //
+    lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
+    if (lpnrLocal == NULL) {
+        printf("WnetOpenEnum failed with error %d\n", dwResult);
+        return FALSE;
+    }
+
+    do {
+        //
+        // Initialize the buffer.
+        //
+        ZeroMemory(lpnrLocal, cbBuffer);
+        //
+        // Call the WNetEnumResource function to continue
+        //  the enumeration.
+        //
+        cEntries = -1;
+        dwResultEnum = WNetEnumResource(hEnum,  // resource handle
+                                        &cEntries,      // defined locally as -1
+                                        lpnrLocal,      // LPNETRESOURCE
+                                        &cbBuffer);     // buffer size
+        //
+        // If the call succeeds, loop through the structures.
+        //
+        if (dwResultEnum == NO_ERROR) {
+            for (i = 0; i < cEntries; i++) {
+                // Call an application-defined function to
+                //  display the contents of the NETRESOURCE structures.
+                //
+                DisplayStruct(i, &lpnrLocal[i]);
+
+                // If the NETRESOURCE structure represents a container resource,
+                //  call the EnumerateFunc function recursively.
+
+                if (dwScope == RESOURCE_GLOBALNET &&
+                     RESOURCEUSAGE_CONTAINER == (lpnrLocal[i].dwUsage
+                                                & RESOURCEUSAGE_CONTAINER))
+                    if (!EnumerateFunc(dwScope, &lpnrLocal[i]))
+                        printf("EnumerateFunc returned FALSE\n");
+            }
+        }
+        // Process errors.
+        //
+        else if (dwResultEnum != ERROR_NO_MORE_ITEMS) {
+            printf("WNetEnumResource failed with error %d\n", dwResultEnum);
+            break;
+        }
+    }
+    //
+    // End do.
+    //
+    while (dwResultEnum != ERROR_NO_MORE_ITEMS);
+    //
+    // Call the GlobalFree function to free the memory.
+    //
+    GlobalFree((HGLOBAL) lpnrLocal);
+    //
+    // Call WNetCloseEnum to end the enumeration.
+    //
+    dwResult = WNetCloseEnum(hEnum);
+
+    if (dwResult != NO_ERROR) {
+        //
+        // Process errors.
+        //
+        printf("WNetCloseEnum failed with error %d\n\n", dwResult);
+//    NetErrorHandler(hwnd, dwResult, (LPSTR)"WNetCloseEnum");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+void DisplayStruct(int i, LPNETRESOURCE lpnrLocal)
+{
+    printf("NETRESOURCE[%d] Scope: ", i);
+    switch (lpnrLocal->dwScope) {
+    case (RESOURCE_CONNECTED):
+        printf("connected\n");
+        break;
+    case (RESOURCE_GLOBALNET):
+        printf("all resources\n");
+        break;
+    case (RESOURCE_REMEMBERED):
+        printf("remembered\n");
+        break;
+    case RESOURCE_RECENT:
+        printf("recent\n");
+        break;
+    case RESOURCE_CONTEXT:
+        printf("context\n");
+        break;
+    default:
+        printf("unknown scope %d\n", lpnrLocal->dwScope);
+        break;
+    }
+
+    printf("NETRESOURCE[%d] Type: ", i);
+    switch (lpnrLocal->dwType) {
+    case (RESOURCETYPE_ANY):
+        printf("any\n");
+        break;
+    case (RESOURCETYPE_DISK):
+        printf("disk\n");
+        break;
+    case (RESOURCETYPE_PRINT):
+        printf("print\n");
+        break;
+    case RESOURCETYPE_RESERVED:
+        printf("reserved\n");
+        break;
+    default:
+        printf("unknown type %d\n", lpnrLocal->dwType);
+        break;
+    }
+
+    printf("NETRESOURCE[%d] DisplayType: ", i);
+    switch (lpnrLocal->dwDisplayType) {
+    case (RESOURCEDISPLAYTYPE_GENERIC):
+        printf("generic\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_DOMAIN):
+        printf("domain\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_SERVER):
+        printf("server\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_SHARE):
+        printf("share\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_FILE):
+        printf("file\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_GROUP):
+        printf("group\n");
+        break;
+    case (RESOURCEDISPLAYTYPE_NETWORK):
+        printf("network\n");
+        break;
+    case RESOURCEDISPLAYTYPE_ROOT:
+        printf("root\n");
+        break;
+    case RESOURCEDISPLAYTYPE_SHAREADMIN:
+        printf("share-admin\n");
+        break;
+    case RESOURCEDISPLAYTYPE_DIRECTORY:
+        printf("directory\n");
+        break;
+    case RESOURCEDISPLAYTYPE_TREE:
+        printf("tree\n");
+        break;
+    case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+        printf("nds-container\n");
+        break;
+    default:
+        printf("unknown display type %d\n", lpnrLocal->dwDisplayType);
+        break;
+    }
+
+    printf("NETRESOURCE[%d] Usage: 0x%x = ", i, lpnrLocal->dwUsage);
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_CONNECTABLE)
+        printf("connectable ");
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_CONTAINER)
+        printf("container ");
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_NOLOCALDEVICE)
+        printf("no-local ");
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_SIBLING)
+        printf("sibling ");
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_ATTACHED)
+        printf("attached ");
+    if (lpnrLocal->dwUsage & RESOURCEUSAGE_RESERVED)
+        printf("reserved ");
+    printf("\n");
+
+    printf("NETRESOURCE[%d] Localname: %S\n", i, lpnrLocal->lpLocalName);
+    printf("NETRESOURCE[%d] Remotename: %S\n", i, lpnrLocal->lpRemoteName);
+    printf("NETRESOURCE[%d] Comment: %S\n", i, lpnrLocal->lpComment);
+    printf("NETRESOURCE[%d] Provider: %S\n", i, lpnrLocal->lpProvider);
+    printf("\n");
+}
+
+
+/*
+BOOL WINAPI NetErrorHandler(HWND hwnd,
+                            DWORD dwErrorCode,
+                            LPSTR lpszFunction)
+{
+    DWORD dwWNetResult, dwLastError;
+    CHAR szError[256];
+    CHAR szCaption[256];
+    CHAR szDescription[256];
+    CHAR szProvider[256];
+
+    // The following code performs standard error-handling.
+
+    if (dwErrorCode != ERROR_EXTENDED_ERROR)
+    {
+        sprintf_s((LPSTR) szError, sizeof(szError), "%s failed; \nResult is %ld",
+            lpszFunction, dwErrorCode);
+        sprintf_s((LPSTR) szCaption, sizeof(szCaption), "%s error", lpszFunction);
+        MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);
+        return TRUE;
+    }
+
+    // The following code performs error-handling when the
+    //  ERROR_EXTENDED_ERROR return value indicates that the
+    //  WNetGetLastError function can retrieve additional information.
+
+    else
+    {
+        dwWNetResult = WNetGetLastError(&dwLastError, // error code
+            (LPSTR) szDescription,  // buffer for error description
+            sizeof(szDescription),  // size of error buffer
+            (LPSTR) szProvider,     // buffer for provider name
+            sizeof(szProvider));    // size of name buffer
+
+        //
+        // Process errors.
+        //
+        if(dwWNetResult != NO_ERROR) {
+            sprintf_s((LPSTR) szError, sizeof(szError),
+                "WNetGetLastError failed; error %ld", dwWNetResult);
+            MessageBox(hwnd, (LPSTR) szError,
+                "WNetGetLastError", MB_OK);
+            return FALSE;
+        }
+
+        //
+        // Otherwise, print the additional error information.
+        //
+        sprintf_((LPSTR) szError, sizeof(szError),
+            "%s failed with code %ld;\n%s",
+            (LPSTR) szProvider, dwLastError, (LPSTR) szDescription);
+        sprintf_s((LPSTR) szCaption, sizeof(szCaption), "%s error", lpszFunction);
+        MessageBox(hwnd, (LPSTR) szError, (LPSTR) szCaption, MB_OK);
+        return TRUE;
+    }
+}
+*/