summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/rpl
diff options
context:
space:
mode:
Diffstat (limited to 'private/net/svcdlls/rpl')
-rw-r--r--private/net/svcdlls/rpl/client/makefile6
-rw-r--r--private/net/svcdlls/rpl/client/rplapi.c1893
-rw-r--r--private/net/svcdlls/rpl/client/rplbind.c119
-rw-r--r--private/net/svcdlls/rpl/client/rplstub.c1279
-rw-r--r--private/net/svcdlls/rpl/client/sources62
-rw-r--r--private/net/svcdlls/rpl/command/makefile6
-rw-r--r--private/net/svcdlls/rpl/command/makefile.inc4
-rw-r--r--private/net/svcdlls/rpl/command/rplcmd.c2408
-rw-r--r--private/net/svcdlls/rpl/command/rplmsg.mc610
-rw-r--r--private/net/svcdlls/rpl/command/rplver.rc12
-rw-r--r--private/net/svcdlls/rpl/command/sources65
-rw-r--r--private/net/svcdlls/rpl/convert/adapter.c71
-rw-r--r--private/net/svcdlls/rpl/convert/boot.c306
-rw-r--r--private/net/svcdlls/rpl/convert/config.c527
-rw-r--r--private/net/svcdlls/rpl/convert/convert.rc12
-rw-r--r--private/net/svcdlls/rpl/convert/debug.c76
-rw-r--r--private/net/svcdlls/rpl/convert/library.c330
-rw-r--r--private/net/svcdlls/rpl/convert/local.h227
-rw-r--r--private/net/svcdlls/rpl/convert/makefile6
-rw-r--r--private/net/svcdlls/rpl/convert/makefile.inc4
-rw-r--r--private/net/svcdlls/rpl/convert/nlstxt.mc214
-rw-r--r--private/net/svcdlls/rpl/convert/profile.c270
-rw-r--r--private/net/svcdlls/rpl/convert/rpldata.h47
-rw-r--r--private/net/svcdlls/rpl/convert/rplmain.c740
-rw-r--r--private/net/svcdlls/rpl/convert/sources51
-rw-r--r--private/net/svcdlls/rpl/convert/vendor.c139
-rw-r--r--private/net/svcdlls/rpl/convert/wksta.c565
-rw-r--r--private/net/svcdlls/rpl/dirs33
-rw-r--r--private/net/svcdlls/rpl/dll/buffer.c124
-rw-r--r--private/net/svcdlls/rpl/dll/emulator.c110
-rw-r--r--private/net/svcdlls/rpl/dll/ether.c498
-rw-r--r--private/net/svcdlls/rpl/dll/fdr.c181
-rw-r--r--private/net/svcdlls/rpl/dll/find.c312
-rw-r--r--private/net/svcdlls/rpl/dll/found.c159
-rw-r--r--private/net/svcdlls/rpl/dll/init.c545
-rw-r--r--private/net/svcdlls/rpl/dll/local.h498
-rw-r--r--private/net/svcdlls/rpl/dll/makefile6
-rw-r--r--private/net/svcdlls/rpl/dll/report.c60
-rw-r--r--private/net/svcdlls/rpl/dll/sfr.c499
-rw-r--r--private/net/svcdlls/rpl/dll/sizeof.c174
-rw-r--r--private/net/svcdlls/rpl/dll/sources37
-rw-r--r--private/net/svcdlls/rpl/dll/xns.c520
-rw-r--r--private/net/svcdlls/rpl/doc/lm21ddk.docbin0 -> 168367 bytes
-rw-r--r--private/net/svcdlls/rpl/doc/rpl.docbin0 -> 385024 bytes
-rw-r--r--private/net/svcdlls/rpl/exe/makefile6
-rw-r--r--private/net/svcdlls/rpl/exe/rplsvc.c146
-rw-r--r--private/net/svcdlls/rpl/exe/rplsvc.rc11
-rw-r--r--private/net/svcdlls/rpl/exe/sources97
-rw-r--r--private/net/svcdlls/rpl/imports.idl63
-rw-r--r--private/net/svcdlls/rpl/inc/adapter.h52
-rw-r--r--private/net/svcdlls/rpl/inc/boot.h59
-rw-r--r--private/net/svcdlls/rpl/inc/config.h95
-rw-r--r--private/net/svcdlls/rpl/inc/i_lmrpl.h26
-rw-r--r--private/net/svcdlls/rpl/inc/imports.h45
-rw-r--r--private/net/svcdlls/rpl/inc/profile.h62
-rw-r--r--private/net/svcdlls/rpl/inc/riplcons.h130
-rw-r--r--private/net/svcdlls/rpl/inc/ripltyps.h137
-rw-r--r--private/net/svcdlls/rpl/inc/rpldb.h30
-rw-r--r--private/net/svcdlls/rpl/inc/rpldebug.h88
-rw-r--r--private/net/svcdlls/rpl/inc/rpldll.h144
-rw-r--r--private/net/svcdlls/rpl/inc/rpllib.h94
-rw-r--r--private/net/svcdlls/rpl/inc/rplnames.h24
-rw-r--r--private/net/svcdlls/rpl/inc/vendor.h53
-rw-r--r--private/net/svcdlls/rpl/inc/wksta.h105
-rw-r--r--private/net/svcdlls/rpl/lib/adapter.c120
-rw-r--r--private/net/svcdlls/rpl/lib/addkey.c31
-rw-r--r--private/net/svcdlls/rpl/lib/cmdline.c249
-rw-r--r--private/net/svcdlls/rpl/lib/jeterror.c66
-rw-r--r--private/net/svcdlls/rpl/lib/local.h54
-rw-r--r--private/net/svcdlls/rpl/lib/makefile6
-rw-r--r--private/net/svcdlls/rpl/lib/reg.c271
-rw-r--r--private/net/svcdlls/rpl/lib/report.c111
-rw-r--r--private/net/svcdlls/rpl/lib/sources36
-rw-r--r--private/net/svcdlls/rpl/lib/tcpip.c68
-rw-r--r--private/net/svcdlls/rpl/makefil073
-rw-r--r--private/net/svcdlls/rpl/makefile12
-rw-r--r--private/net/svcdlls/rpl/rplcnv/local.h64
-rw-r--r--private/net/svcdlls/rpl/rplcnv/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplcnv/rplcnv.c37
-rw-r--r--private/net/svcdlls/rpl/rplcnv/sources27
-rw-r--r--private/net/svcdlls/rpl/rplinst/debug.c137
-rw-r--r--private/net/svcdlls/rpl/rplinst/debug.h28
-rw-r--r--private/net/svcdlls/rpl/rplinst/local.h77
-rw-r--r--private/net/svcdlls/rpl/rplinst/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplinst/makefile.inc8
-rw-r--r--private/net/svcdlls/rpl/rplinst/querydir.c136
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplimsg.h55
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplinst.c137
-rw-r--r--private/net/svcdlls/rpl/rplinst/rplinst.rc14
-rw-r--r--private/net/svcdlls/rpl/rplinst/security.c1567
-rw-r--r--private/net/svcdlls/rpl/rplinst/security.h17
-rw-r--r--private/net/svcdlls/rpl/rplinst/sources38
-rw-r--r--private/net/svcdlls/rpl/rplinst/tree.c2
-rw-r--r--private/net/svcdlls/rpl/rplinst/tree.h28
-rw-r--r--private/net/svcdlls/rpl/rplinst/treei.h51
-rw-r--r--private/net/svcdlls/rpl/rplstart/local.h66
-rw-r--r--private/net/svcdlls/rpl/rplstart/makefile6
-rw-r--r--private/net/svcdlls/rpl/rplstart/rplstart.c139
-rw-r--r--private/net/svcdlls/rpl/rplstart/sources32
-rw-r--r--private/net/svcdlls/rpl/rplsvc.idl543
-rw-r--r--private/net/svcdlls/rpl/rplsvc_c.acf33
-rw-r--r--private/net/svcdlls/rpl/rplsvc_s.acf7
-rw-r--r--private/net/svcdlls/rpl/server/adapter.c502
-rw-r--r--private/net/svcdlls/rpl/server/apisec.c124
-rw-r--r--private/net/svcdlls/rpl/server/apisec.h52
-rw-r--r--private/net/svcdlls/rpl/server/bbcfile.c1059
-rw-r--r--private/net/svcdlls/rpl/server/bbcfile.h28
-rw-r--r--private/net/svcdlls/rpl/server/boot.c832
-rw-r--r--private/net/svcdlls/rpl/server/config.c711
-rw-r--r--private/net/svcdlls/rpl/server/database.c998
-rw-r--r--private/net/svcdlls/rpl/server/database.h33
-rw-r--r--private/net/svcdlls/rpl/server/db.h263
-rw-r--r--private/net/svcdlls/rpl/server/dblib.c386
-rw-r--r--private/net/svcdlls/rpl/server/dblib.h66
-rw-r--r--private/net/svcdlls/rpl/server/debug.c141
-rw-r--r--private/net/svcdlls/rpl/server/debug.h28
-rw-r--r--private/net/svcdlls/rpl/server/disk.c318
-rw-r--r--private/net/svcdlls/rpl/server/fitfile.c442
-rw-r--r--private/net/svcdlls/rpl/server/fitfile.h27
-rw-r--r--private/net/svcdlls/rpl/server/library.c273
-rw-r--r--private/net/svcdlls/rpl/server/local.h556
-rw-r--r--private/net/svcdlls/rpl/server/makefile6
-rw-r--r--private/net/svcdlls/rpl/server/memory.c188
-rw-r--r--private/net/svcdlls/rpl/server/open.c600
-rw-r--r--private/net/svcdlls/rpl/server/open.h27
-rw-r--r--private/net/svcdlls/rpl/server/profile.c1139
-rw-r--r--private/net/svcdlls/rpl/server/read.c409
-rw-r--r--private/net/svcdlls/rpl/server/read.h29
-rw-r--r--private/net/svcdlls/rpl/server/report.c199
-rw-r--r--private/net/svcdlls/rpl/server/report.h34
-rw-r--r--private/net/svcdlls/rpl/server/request.c697
-rw-r--r--private/net/svcdlls/rpl/server/request.h26
-rw-r--r--private/net/svcdlls/rpl/server/resume.c308
-rw-r--r--private/net/svcdlls/rpl/server/resume.h61
-rw-r--r--private/net/svcdlls/rpl/server/rpldata.h161
-rw-r--r--private/net/svcdlls/rpl/server/rplmain.c982
-rw-r--r--private/net/svcdlls/rpl/server/rplrest.c36
-rw-r--r--private/net/svcdlls/rpl/server/rplsvc.c122
-rw-r--r--private/net/svcdlls/rpl/server/rplsvc.rc12
-rw-r--r--private/net/svcdlls/rpl/server/security.c1005
-rw-r--r--private/net/svcdlls/rpl/server/service.c207
-rw-r--r--private/net/svcdlls/rpl/server/setup.c716
-rw-r--r--private/net/svcdlls/rpl/server/setup.h34
-rw-r--r--private/net/svcdlls/rpl/server/sources83
-rw-r--r--private/net/svcdlls/rpl/server/tree.c353
-rw-r--r--private/net/svcdlls/rpl/server/tree.h28
-rw-r--r--private/net/svcdlls/rpl/server/treei.h47
-rw-r--r--private/net/svcdlls/rpl/server/vendor.c528
-rw-r--r--private/net/svcdlls/rpl/server/wcst.c54
-rw-r--r--private/net/svcdlls/rpl/server/wksta.c1471
-rw-r--r--private/net/svcdlls/rpl/server/worker.c572
-rw-r--r--private/net/svcdlls/rpl/server/worker.h35
152 files changed, 36638 insertions, 0 deletions
diff --git a/private/net/svcdlls/rpl/client/makefile b/private/net/svcdlls/rpl/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/client/rplapi.c b/private/net/svcdlls/rpl/client/rplapi.c
new file mode 100644
index 000000000..881161ea2
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplapi.c
@@ -0,0 +1,1893 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rpl.c
+
+Abstract:
+
+ This file contains program to test RPL APIs.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 19-November-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmrpl.h> // RPL_HANDLE
+#include <i_lmrpl.h> // RPL_CONFIG_INFO_1
+#include <lmapibuf.h> // NetApiBufferFree()
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <ctype.h> // toupper - bombs if I use it
+
+#define RPL_BUFFER_GET_ALL ((DWORD)-1)
+
+#define Call( fn ) \
+ { \
+ int _ApiError = fn; \
+ if ( _ApiError != NO_ERROR) { \
+ printf( "Line = %d, _ApiError = %d\n", __LINE__, _ApiError); \
+ DbgPrint( "[RplApi] Line = %d, _ApiError = %d\n", __LINE__, _ApiError); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ printf( "File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgPrint( "[RplApi] File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+
+PWCHAR G_ServerName; // GLOBAL
+RPL_HANDLE G_ServerHandle; // GLOBAL
+
+
+BOOL ReadString(
+ IN PCHAR StringName,
+ OUT PWCHAR * pString,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ WCHAR WcharBuffer[ 300];
+ DWORD Length; // includes terminal null wchar
+
+ printf( "%s=", StringName);
+
+ *pString = NULL;
+
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( *Line == 0) {
+ //
+ // Zero length input is OK only when input is optional (and in that
+ // case pointer is assumed to be NULL).
+ //
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", StringName);
+ return( FALSE);
+ }
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, Line,
+ -1, WcharBuffer, sizeof( WcharBuffer));
+ if ( Length == 0) {
+ printf( "Invalid string = %s\n, Line");
+ return( FALSE);
+ }
+ *pString = LocalAlloc( GMEM_FIXED, Length * sizeof(WCHAR));
+ if ( *pString == NULL) {
+ printf( "LocalAlloc failed");
+ return( FALSE);
+ }
+ wcscpy( *pString, WcharBuffer);
+ return( TRUE);
+}
+
+
+BOOL ReadInt(
+ IN PCHAR FieldName,
+ OUT int * pInt,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ printf( "%s=", FieldName);
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( sscanf( Line, "%d", pInt) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", FieldName);
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL ReadWchar(
+ IN PCHAR FieldName,
+ OUT PWCHAR pWchar,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ CHAR Char;
+
+ printf( "%s=", FieldName);
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ //
+ // Note that "%1s" instead of "%1c" would nicely overwrite stack
+ // (due to terminating NULL added).
+ //
+ if ( sscanf( Line, "%1c", &Char) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ printf( "%s must be supplied\n", FieldName);
+ return( FALSE);
+ }
+ }
+ *pWchar = Char;
+ return( TRUE);
+}
+
+
+VOID TestConnect( VOID)
+{
+ RPL_HANDLE ServerHandle;
+ Call( NetRplOpen( G_ServerName, &ServerHandle);)
+ printf( "ServerHandle = 0x%x\n", ServerHandle);
+ Call( NetRplClose( ServerHandle);)
+}
+
+
+DWORD ConfigDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "ConfigName=%ws\n\tConfigComment=%ws\n", Info->ConfigName, Info->ConfigComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags = 0x%x\n", Info->Flags);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBootName=%ws\n\tDirName=%ws\n\tDirname2=%ws\n\t"
+ "Dirname3=%ws\n\tDirName4=%ws\n\tFitShared=%ws\n\tFitPersonal=%ws\n",
+ Info->BootName, Info->DirName, Info->DirName2,
+ Info->DirName3, Info->DirName4, Info->FitShared, Info->FitPersonal);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestConfigEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ break;
+ default:
+ printf( "\nTestConfigEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestConfigEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( AdapterName != NULL) {
+ printf( ", AdapterName=%ws", AdapterName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplConfigEnum( G_ServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ConfigDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD ProfileDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "ProfileName=%ws\n\tProfileComment=%ws\n", Info->ProfileName, Info->ProfileComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags = 0x%x\n", Info->Flags);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tConfigName=%ws\n\tBootName=%ws\n\tFitShared=%ws\n\tFitPersonal=%ws\n",
+ Info->ConfigName, Info->BootName, Info->FitShared, Info->FitPersonal);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestProfileEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ break;
+ default:
+ printf( "\nTestProfileEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestProfileEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( AdapterName != NULL) {
+ printf( ", AdapterName=%ws", AdapterName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplProfileEnum( G_ServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ProfileDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD ServiceDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_INFO_0 Info = Buffer;
+
+ if ( Level > 0) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "Flags = 0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+DWORD WkstaDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "WkstaName=%ws\n\tWkstaComment=%ws\n", Info->WkstaName, Info->WkstaComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n\tProfileName=%ws\n", Info->Flags, Info->ProfileName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBootName=%ws\n", Info->BootName);
+ printf( "\tFitFile=%ws\n", Info->FitFile);
+ printf( "\tAdapterName=%ws\n", Info->AdapterName);
+ printf( "\tTcpIpAddress=0x%x\n", Info->TcpIpAddress);
+ printf( "\tTcpIpSubnet=0x%x\n", Info->TcpIpSubnet);
+ printf( "\tTcpIpGateway=0x%x\n", Info->TcpIpGateway);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestWkstaEnum(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ break;
+ default:
+ printf( "\nTestWkstaEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestWkstaEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( ProfileName != NULL) {
+ printf( ", ProfileName=%ws", ProfileName);
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplWkstaEnum( G_ServerHandle, ProfileName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ WkstaDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+DWORD VendorDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "VendorName=%ws\n\tVendorComment=%ws\n", Info->VendorName, Info->VendorComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+DWORD BootDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "BootName=%ws\n\tBootComment=%ws\n", Info->BootName, Info->BootComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n\tVendorName=%ws\n", Info->Flags, Info->VendorName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ printf( "\tBbcFile=%ws\n", Info->BbcFile);
+ printf( "\tWindowSize=0x%x\n", Info->WindowSize);
+ return( NO_ERROR);
+}
+
+
+
+DWORD AdapterDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ printf( "AdapterName=%ws\n\tAdapterComment=%ws\n", Info->AdapterName, Info->AdapterComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ printf( "\tFlags=0x%x\n", Info->Flags);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestAdapterEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ break;
+ default:
+ printf( "\nTestAdapterEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestAdapterEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplAdapterEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ AdapterDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID TestAdapterAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestAdapterAdd\n");
+ AdapterDisplayInfo( 1, Buffer);
+ Error = NetRplAdapterAdd( G_ServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID AdapterAdd( VOID)
+{
+ RPL_ADAPTER_INFO_1 Info;
+
+ if ( !ReadString( "AdapterName", &Info.AdapterName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "AdapterComment", &Info.AdapterComment, FALSE)) {
+ return;
+ }
+ TestAdapterAdd( &Info);
+}
+
+
+VOID TestBootAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestBootAdd\n");
+ BootDisplayInfo( 2, Buffer);
+ Error = NetRplBootAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID BootAdd( VOID)
+{
+ RPL_BOOT_INFO_2 Info;
+
+ if ( !ReadString( "BootName", &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "VendorName", &Info.VendorName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ConfigName", &Info.BbcFile, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "BootComment", &Info.BootComment, FALSE)) {
+ return;
+ }
+ Info.WindowSize = 1;
+ if ( !ReadInt( "WindowSize", &Info.WindowSize, FALSE)) {
+ return;
+ }
+ TestBootAdd( &Info);
+}
+
+
+VOID BootDel( VOID)
+{
+ PWCHAR BootName;
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( "BootName", &BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "VendorName", &VendorName, TRUE)) {
+ return;
+ }
+ Error = NetRplBootDel( G_ServerHandle, BootName, VendorName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID TestBootEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_BOOT_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_BOOT_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_BOOT_INFO_2);
+ break;
+ default:
+ printf( "\nTestBootEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestBootEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplBootEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ BootDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID BootEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestBootEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestBootEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestConfigAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestConfigAdd\n");
+ ConfigDisplayInfo( 2, Buffer);
+ Error = NetRplConfigAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID ConfigAdd( VOID)
+{
+ RPL_CONFIG_INFO_2 Info;
+
+ if ( !ReadString( "ConfigName", &Info.ConfigName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName", &Info.DirName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "FitShared", &Info.FitShared, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.FitPersonal, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "ConfigComment", &Info.ConfigComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "DirName2", &Info.DirName2, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName3", &Info.DirName3, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "DirName4", &Info.DirName4, TRUE)) {
+ return;
+ }
+ TestConfigAdd( &Info);
+}
+
+
+VOID ConfigDel( VOID)
+{
+ PWCHAR ConfigName;
+ DWORD Error;
+
+ if ( !ReadString( "ConfigName", &ConfigName, FALSE)) {
+ return;
+ }
+ Error = NetRplConfigDel( G_ServerHandle, ConfigName);
+ if ( Error != 0) {
+ printf( "Failed to delete ConfigName=%ws, Error = %d\n", ConfigName, Error);
+ }
+}
+
+
+VOID TestVendorAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestVendorAdd\n");
+ VendorDisplayInfo( 1, Buffer);
+ Error = NetRplVendorAdd( G_ServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID VendorAdd( VOID)
+{
+ RPL_VENDOR_INFO_1 Info;
+
+ if ( !ReadString( "VendorName", &Info.VendorName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "VendorComment", &Info.VendorComment, FALSE)) {
+ return;
+ }
+ TestVendorAdd( &Info);
+}
+
+
+VOID VendorDel( VOID)
+{
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( "VendorName", &VendorName, FALSE)) {
+ return;
+ }
+ Error = NetRplVendorDel( G_ServerHandle, VendorName);
+ if ( Error != 0) {
+ printf( "Failed to delete VendorName=%ws, Error = %d\n", VendorName, Error);
+ }
+}
+
+
+VOID TestVendorEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ break;
+ default:
+ printf( "\nTestVendorEnum: invalid Level=%d", Level);
+ return;
+ break;
+ }
+
+ printf( "\nTestVendorEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ printf( ", unlimited buffer size");
+ }
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle=0x%x\n\n", *pResumeHandle);
+ } else {
+ printf( ", not resumable.\n\n");
+ }
+
+ for ( ; ; ) {
+ Error = NetRplVendorEnum( G_ServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ printf( "Error = %d\n", Error);
+ break;
+ }
+
+ printf( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries);
+ if ( pResumeHandle != NULL) {
+ printf( ", ResumeHandle = 0x%x\n", *pResumeHandle);
+ } else {
+ printf("\n");
+ }
+
+ for ( index = 0; index < EntriesRead; index++) {
+ VendorDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ RPL_ASSERT( Error == NO_ERROR);
+ break;
+ }
+ RPL_ASSERT( Error == ERROR_MORE_DATA);
+ }
+}
+
+
+VOID VendorEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestVendorEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestVendorEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestProfileAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestProfileAdd");
+ ProfileDisplayInfo( 2, Buffer);
+ Error = NetRplProfileAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestProfileGetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ printf( "\nTestProfileGetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName);
+ Error = NetRplProfileGetInfo( G_ServerHandle, ProfileName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ ProfileDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestProfileSetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestProfileSetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName);
+ ProfileDisplayInfo( Level, Buffer);
+ Error = NetRplProfileSetInfo( G_ServerHandle, ProfileName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestWkstaAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestWkstaAdd\n");
+ WkstaDisplayInfo( 2, Buffer);
+ Error = NetRplWkstaAdd( G_ServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID TestWkstaGetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ printf( "\nTestWkstaGetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName);
+ Error = NetRplWkstaGetInfo( G_ServerHandle, WkstaName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ WkstaDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestWkstaSetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ printf( "\nTestWkstaSetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName);
+ WkstaDisplayInfo( Level, Buffer);
+ Error = NetRplWkstaSetInfo( G_ServerHandle, WkstaName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID AdapterDel( VOID)
+{
+ PWCHAR AdapterName;
+ DWORD Error;
+
+ if ( !ReadString( "AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( AdapterName == NULL) {
+ printf( "You requested to delete all adapters!\n");
+ }
+
+ Error = NetRplAdapterDel( G_ServerHandle, AdapterName);
+ if ( Error != 0) {
+ printf( "Failed to delete AdapterName=%ws, Error = %d\n", AdapterName, Error);
+ }
+}
+
+
+VOID AdapterEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestAdapterEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestAdapterEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ConfigEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestConfigEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestConfigEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileAdd( VOID)
+{
+ RPL_PROFILE_INFO_2 Info;
+
+ if ( !ReadString( "ProfileName", &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ConfigName", &Info.ConfigName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "ProfileComment", &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitShared", &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.FitPersonal, FALSE)) {
+ return;
+ }
+ TestProfileAdd( &Info);
+}
+
+
+VOID ProfileClone( VOID)
+{
+ PWCHAR SourceProfileName;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetProfileComment;
+ DWORD Error;
+
+ if ( !ReadString( "SourceProfileName", &SourceProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetProfileName", &TargetProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetProfileComment", &TargetProfileComment, FALSE)) {
+ return;
+ }
+ Error = NetRplProfileClone( G_ServerHandle, SourceProfileName,
+ TargetProfileName, TargetProfileComment);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID ProfileDel( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Error;
+
+ if ( !ReadString( "ProfileName", &ProfileName, TRUE)) {
+ return;
+ }
+ Error = NetRplProfileDel( G_ServerHandle, ProfileName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID ProfileEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ printf( "Input: Level & PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] AdapterName", &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestProfileEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestProfileEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR ProfileName[ 20];
+ CHAR ProfileNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ printf( "Input: Level & ProfileName\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, ProfileNameA);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, ProfileNameA, -1,
+ ProfileName, sizeof( ProfileName));
+ if ( Length == 0) {
+ printf( "Invalid ProfileName = %s\n, ProfileNameA");
+ return;
+ }
+ TestProfileGetInfo( Level, ProfileName);
+}
+
+
+VOID ProfileSetInfo( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Level;
+ RPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+
+ Buffer = &Info;
+
+ if ( !ReadInt( "Level", &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( "ProfileName", &ProfileName, TRUE)) {
+ return;
+ }
+ Info.ProfileName = NULL;
+ // ReadString( "ProfileName", &Info.ProfileName);
+ if ( !ReadString( "ProfileComment", &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ if ( Level == 1) {
+ goto testit;
+ }
+ Info.BootName = NULL;
+ // ReadString( "BootName", &Info.ProfileName);
+ if ( !ReadString( "FitShared", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitPersonal", &Info.ProfileName, FALSE)) {
+ return;
+ }
+testit:
+ TestProfileSetInfo( Level, ProfileName, Buffer);
+}
+
+
+VOID ServiceClose( VOID)
+{
+ Call( NetRplClose( G_ServerHandle);)
+}
+
+
+VOID ServiceGetInfo( VOID)
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplGetInfo( G_ServerHandle, 0, &Buffer);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+ ServiceDisplayInfo( 0, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID ServiceOpen( VOID)
+{
+ Call( NetRplOpen( G_ServerName, &G_ServerHandle);)
+}
+
+
+VOID ServiceSetInfo( VOID)
+{
+ DWORD Level;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ if ( !ReadInt( "Level", &Level, FALSE)) {
+ return;
+ }
+
+ switch( Level) {
+ case 0: {
+ RPL_INFO_0 Info;
+ Buffer = &Info;
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ Error = NetRplSetInfo( G_ServerHandle, 0, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d, ErrorParameter = %d\n", Error, ErrorParameter);
+ return;
+ }
+}
+
+
+VOID WkstaAdd( VOID)
+{
+ RPL_WKSTA_INFO_2 Info;
+
+ if ( !ReadString( "WkstaName", &Info.WkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "ProfileName", &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "AdapterName", &Info.AdapterName, TRUE)) {
+ return;
+ }
+ if ( !ReadInt( "Flags", &Info.Flags, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "BootName", &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "FitFile", &Info.FitFile, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( "TcpIpAddress", &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( "TcpIpSubnet", &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( "TcpIpGateway", &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+ TestWkstaAdd( &Info);
+}
+
+
+VOID WkstaClone( VOID)
+{
+ PWCHAR SourceWkstaName;
+ PWCHAR TargetWkstaName;
+ PWCHAR TargetWkstaComment;
+ PWCHAR TargetAdapterName;
+ DWORD TargetTcpIpAddress;
+ DWORD Error;
+
+ if ( !ReadString( "SourceWkstaName", &SourceWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetWkstaName", &TargetWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( "TargetAdapterName", &TargetAdapterName, TRUE)) {
+ return;
+ }
+ printf( "\tAll other parameters are optional\n");
+ if ( !ReadString( "TargetWkstaComment", &TargetWkstaComment, FALSE)) {
+ return;
+ }
+ TargetTcpIpAddress = (DWORD)-1;
+ if( !ReadInt( "TcpIpAddress", &TargetTcpIpAddress, FALSE)) {
+ return;
+ }
+ Error = NetRplWkstaClone( G_ServerHandle, SourceWkstaName,
+ TargetWkstaName, TargetWkstaComment, TargetAdapterName, TargetTcpIpAddress);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID WkstaDel( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Error;
+
+ if ( !ReadString( "WkstaName", &WkstaName, TRUE)) {
+ return;
+ }
+ Error = NetRplWkstaDel( G_ServerHandle, WkstaName);
+ if ( Error != NO_ERROR) {
+ printf( "Error = %d\n", Error);
+ return;
+ }
+}
+
+
+VOID WkstaEnum( VOID)
+{
+ BYTE Line[ 300];
+ DWORD Count;
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR ProfileName;
+
+ printf( "Input: Level, PrefMaxLength\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %d", &Level, &PrefMaxLength);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ if( !ReadString( "[filter] ProfileName", &ProfileName, FALSE)) {
+ return;
+ }
+ ResumeHandle = 0, TestWkstaEnum( Level, ProfileName, PrefMaxLength, &ResumeHandle);
+}
+
+
+VOID WkstaGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR WkstaName[ 20];
+ CHAR WkstaNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ printf( "Input: Level & WkstaName\n");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, WkstaNameA);
+ if ( Count != 2) {
+ printf( "Bad number of arguments.\n");
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, WkstaNameA, -1,
+ WkstaName, sizeof( WkstaName));
+ if ( Length == 0) {
+ printf( "Invalid WkstaName = %s\n, WkstaNameA");
+ return;
+ }
+ TestWkstaGetInfo( Level, WkstaName);
+}
+
+
+VOID WkstaSetInfo( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Level;
+ LPVOID Buffer;
+
+ if ( !ReadInt( "Level", &Level, FALSE)) {
+ return;
+ }
+ if ( !ReadString( "WkstaName", &WkstaName, FALSE)) {
+ return;
+ }
+
+ switch( Level) {
+ case 0: {
+ RPL_WKSTA_INFO_0 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ break;
+ }
+ case 1: {
+ RPL_WKSTA_INFO_1 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if( !ReadString( "ProfileName", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ break;
+ }
+ case 2: {
+ RPL_WKSTA_INFO_2 Info;
+ Buffer = &Info;
+ Info.WkstaName = NULL;
+ // ReadString( "WkstaName", &Info.WkstaName);
+ if( !ReadString( "WkstaComment", &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if( !ReadString( "ProfileName", &Info.ProfileName, FALSE)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ TestWkstaSetInfo( Level, WkstaName, Buffer);
+}
+
+
+VOID Worker( VOID)
+{
+ BYTE Line[ 300];
+ CHAR response;
+
+ for ( ; ;) {
+ printf("Adapter Boot Config Profile Service Vendor Wksta [Quit]: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ AdapterAdd();
+ break;
+ case 'D':
+ AdapterDel();
+ break;
+ case 'E':
+ AdapterEnum();
+ break;
+ }
+ break;
+ case 'B':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ BootAdd();
+ break;
+ case 'D':
+ BootDel();
+ break;
+ case 'E':
+ BootEnum();
+ break;
+ }
+ break;
+ case 'C':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ ConfigAdd();
+ break;
+ case 'D':
+ ConfigDel();
+ break;
+ case 'E':
+ ConfigEnum();
+ break;
+ }
+ break;
+ case 'P':
+ printf( " Add Clone Del Enum GetInfo SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ ProfileAdd();
+ break;
+ case 'C':
+ ProfileClone();
+ break;
+ case 'D':
+ ProfileDel();
+ break;
+ case 'E':
+ ProfileEnum();
+ break;
+ case 'G':
+ ProfileGetInfo();
+ break;
+ case 'S':
+ ProfileSetInfo();
+ break;
+ }
+ break;
+ case 'S':
+ printf( " Close GetInfo Open SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'C':
+ ServiceClose();
+ break;
+ case 'G':
+ ServiceGetInfo();
+ break;
+ case 'O':
+ ServiceOpen();
+ break;
+ case 'S':
+ ServiceSetInfo();
+ break;
+ }
+ break;
+ case 'V':
+ printf( " Add Del Enum: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ VendorAdd();
+ break;
+ case 'D':
+ VendorDel();
+ break;
+ case 'E':
+ VendorEnum();
+ break;
+ }
+ break;
+ case 'W':
+ printf( " Add Clone Del Enum GetInfo SetInfo: ");
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ sscanf( Line, " %1c", &response);
+ switch( toupper(response)) {
+ case 'A':
+ WkstaAdd();
+ break;
+ case 'C':
+ WkstaClone();
+ break;
+ case 'D':
+ WkstaDel();
+ break;
+ case 'E':
+ WkstaEnum();
+ break;
+ case 'G':
+ WkstaGetInfo();
+ break;
+ case 'S':
+ WkstaSetInfo();
+ break;
+ }
+ break;
+ case 'Q':
+ return;
+ break;
+ default:
+ printf( "Your input '%1c' is invalid. Try again.\n", response);
+ break;
+ }
+ }
+}
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ DWORD ResumeHandle;
+
+ if ( argc == 1) {
+ G_ServerName = NULL;
+ } else if ( argc == 2) {
+ WCHAR Buffer[ CNLEN + 1];
+ DWORD Length;
+ Length = MultiByteToWideChar(
+ CP_OEMCP,
+ MB_PRECOMPOSED,
+ argv[ 1],
+ -1,
+ Buffer,
+ sizeof( Buffer)
+ );
+ if ( Length == 0) {
+ printf( "Invalid ServerName\n");
+ return( 1);
+ }
+ G_ServerName = Buffer;
+ printf( "ServerName = %ws\n", G_ServerName);
+ } else {
+ printf( "Usage: RplApi [ServerName]\n");
+ return( 1);
+ }
+
+ TestConnect();
+
+ Call( NetRplOpen( G_ServerName, &G_ServerHandle);)
+ printf( "G_ServerHandle = 0x%x\n", G_ServerHandle);
+
+// #define NOT_YET
+#ifdef NOT_YET
+ for ( ; ; ) {
+ TestConfigEnum( 0, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, NULL, TestConfigEnum( 1, NULL, 400, &ResumeHandle);
+ ResumeHandle = 0, NULL, TestConfigEnum( 2, NULL, 10, &ResumeHandle);
+
+ TestConfigEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ TestConfigEnum( 1, NULL, 500, NULL);
+ ResumeHandle = 0, TestConfigEnum( 0, NULL, 500, &ResumeHandle);
+
+ TestProfileEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, TestProfileEnum( 0, NULL, 10, &ResumeHandle);
+ ResumeHandle = 0, TestProfileEnum( 1, NULL, 30, &ResumeHandle);
+ ResumeHandle = 0, TestProfileEnum( 2, NULL, 50, &ResumeHandle);
+
+ TestWkstaEnum( 2, NULL, RPL_BUFFER_GET_ALL, NULL);
+ ResumeHandle = 0, TestWkstaEnum( 0, NULL, 10, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 1, NULL, 30, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 2, L"elnk2", 30, &ResumeHandle);
+ ResumeHandle = 0, TestWkstaEnum( 1, L"xxx", 30, &ResumeHandle);
+
+ ResumeHandle = 0, TestAdapterEnum( 0, 30, &ResumeHandle);
+
+ TestWkstaGetInfo( 0, L"elnk2_");
+ TestWkstaGetInfo( 0, L"3sta_");
+ }
+#endif
+
+ Worker();
+
+ Call( NetRplClose( G_ServerHandle);)
+ return(0);
+}
diff --git a/private/net/svcdlls/rpl/client/rplbind.c b/private/net/svcdlls/rpl/client/rplbind.c
new file mode 100644
index 000000000..9a89ee7b4
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplbind.c
@@ -0,0 +1,119 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplbind.c
+
+Abstract:
+
+ Contains the RPC bind and un-bind routines for the Remote (Initial)
+ Program Load service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nt.h> // DbgPrint prototype
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rplsvc_c.h> // generated by the MIDL complier
+#include <rpcutil.h> // NetRpc utils
+#include <netlib.h> // UNUSED macro
+#include <rplnames.h> // RPL_INTERFACE_NAME
+
+
+
+/****************************************************************************/
+handle_t
+RPL_NAME_bind (
+ RPL_NAME ServerName
+ )
+/*++
+
+Routine Description:
+ This routine calls a common bind routine that is shared by all services.
+ This routine is called from the remote boot service client stubs when
+ it is necessary to bind to a server.
+
+Arguments:
+
+ ServerName - A pointer to a string containing the name of the server
+ to bind with.
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the
+ binding is unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t bindingHandle;
+ RPC_STATUS status;
+
+ status = NetpBindRpc (
+ ServerName,
+ RPL_INTERFACE_NAME,
+ 0,
+ &bindingHandle);
+
+#ifdef DEBUG
+ DbgPrint("RPLSVC_HANDLE_bind:NetpBindRpc status=%d\n",status);
+ DbgPrint("RPLSVC_HANDLE_bind: handle=%d\n",bindingHandle);
+#endif
+
+ return( bindingHandle);
+}
+
+
+
+/****************************************************************************/
+void
+RPL_NAME_unbind (
+ RPL_NAME ServerName,
+ handle_t BindingHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine calls a common unbind routine that is shared by
+ all services.
+ This routine is called from the remote boot service client stubs when
+ it is necessary to unbind to a server.
+
+
+Arguments:
+
+ ServerName - This is the name of the server from which to unbind.
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UNUSED(ServerName); // This parameter is not used
+
+#ifdef DEBUG
+ DbgPrint("RPLSVC_HANDLE_unbind: handle=%d\n",BindingHandle);
+#endif
+
+ NetpUnbindRpc ( BindingHandle);
+ return;
+}
+
diff --git a/private/net/svcdlls/rpl/client/rplstub.c b/private/net/svcdlls/rpl/client/rplstub.c
new file mode 100644
index 000000000..89d91f3de
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/rplstub.c
@@ -0,0 +1,1279 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplstub.c
+
+Abstract:
+
+ These are the Remote (Initial) Program Load Service API RPC client stubs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 27-Jul-1993 vladimv
+ Created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windef.h> // DWORD
+#include <winbase.h> // EXCEPTION_STATUS_VIOLATION
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // NetRpc utils
+
+#include <lmsvc.h>
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+
+#include <netlib.h> // NetpServiceIsStarted() (needed by netrpc.h).
+#include <netdebug.h> // needed for netrpc.h
+#include <netrpc.h> // NET_REMOTE macros.
+
+#include <rplsvc_c.h> // generated by the MIDL complier
+
+DWORD RplMapRpcError(
+ IN DWORD RpcError,
+ IN DWORD BadContextError
+ );
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplOpen(
+ IN LPTSTR ServerName,
+ OUT LPRPL_HANDLE ServerHandle
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NET_API_STATUS status = ERROR_SUCCESS;
+
+ RpcTryExcept {
+ *ServerHandle = NULL; // needed for RPC to create new context handle
+ status = NetrRplOpen( ServerName, (LPRPL_RPC_HANDLE)ServerHandle);
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR){
+ ServerHandle = NULL;
+ }
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplClose(
+ IN RPL_HANDLE ServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplClose( (LPRPL_RPC_HANDLE)&ServerHandle);
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetRplGetInfo.
+
+Arguments:
+
+ ServerHandle - Handle obtained through NetRplOpen().
+
+ InfoLevel - This indicates the level of information that is desired.
+
+ PointerToBuffer - Pointer to a Location where the pointer to the returned
+ information structure is to be placed.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This is the DLL entrypoint for NetRplSetInfo.
+
+Arguments:
+
+ ServerHandle - Handle obtained through NetRplOpen().
+
+ InfoLevel - This indicates the level of information that is desired.
+
+ Buffer - Pointer to the information structure to be set. InfoLevel describes
+ the structure in this buffer.
+
+ ErrorParameter - - Returns the identifier to the invalid parameter in Buffer if this
+ function returns ERROR_INVALID_PARAMETER.
+
+Return Value:
+
+ NET_API_STATUS - NERR_Success or reason for failure.
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// BOOT block apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplBootAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_BOOT_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// NetRplBootDel: if VendorName is NULL, then all boot block records with
+// given BootName will be deleted.
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR BootName,
+ IN LPTSTR VendorName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplBootDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ BootName,
+ VendorName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplBootEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplBootEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_BOOT_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// CONFIG apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplConfigAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_CONFIG_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ConfigName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplConfigDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ConfigName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplConfigEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplConfigEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName,
+ (LPRPL_CONFIG_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// PROFILE apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR SourceProfileName,
+ IN LPTSTR TargetProfileName,
+ IN LPTSTR TargetProfileComment OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileClone(
+ (RPL_RPC_HANDLE)ServerHandle,
+ SourceProfileName,
+ TargetProfileName,
+ TargetProfileComment
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplProfileEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName,
+ (LPRPL_PROFILE_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplProfileGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplProfileSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplProfileSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ InfoLevel,
+ (LPRPL_PROFILE_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// VENDOR apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplVendorAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_VENDOR_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR VendorName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplVendorDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ VendorName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplVendorEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplVendorEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_VENDOR_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+//
+// ADAPTER apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplAdapterAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_ADAPTER_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR AdapterName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplAdapterDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ AdapterName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplAdapterEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplAdapterEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ (LPRPL_ADAPTER_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+//
+// WKSTA apis
+//
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaAdd(
+ (RPL_RPC_HANDLE)ServerHandle,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR SourceWkstaName,
+ IN LPTSTR TargetWkstaName,
+ IN LPTSTR TargetWkstaComment OPTIONAL,
+ IN LPTSTR TargetAdapterName,
+ IN DWORD TargetWkstaIpAddress
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaClone(
+ (RPL_RPC_HANDLE)ServerHandle,
+ SourceWkstaName,
+ TargetWkstaName,
+ TargetWkstaComment,
+ TargetAdapterName,
+ TargetWkstaIpAddress
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaDel(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR ProfileName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD ResumeHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = InfoLevel;
+
+ RpcTryExcept {
+ status = NetrRplWkstaEnum(
+ (RPL_RPC_HANDLE)ServerHandle,
+ ProfileName,
+ (LPRPL_WKSTA_ENUM)&InfoStruct,
+ PrefMaxLength,
+ TotalEntries,
+ ResumeHandle
+ );
+ if ( status == NERR_Success || status == ERROR_MORE_DATA) {
+ *PointerToBuffer = (LPBYTE)GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName,
+ IN DWORD InfoLevel,
+ OUT LPBYTE * PointerToBuffer
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ *PointerToBuffer = NULL; // Must be NULL so RPC knows to fill it in.
+ status = NetrRplWkstaGetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)PointerToBuffer
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplWkstaSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName,
+ IN DWORD InfoLevel,
+ IN LPBYTE Buffer,
+ OUT LPDWORD ErrorParameter OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplWkstaSetInfo(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ InfoLevel,
+ (LPRPL_WKSTA_INFO_STRUCT)&Buffer,
+ ErrorParameter
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetRplSetSecurity(
+ IN RPL_HANDLE ServerHandle,
+ IN LPTSTR WkstaName OPTIONAL,
+ IN DWORD WkstaRid,
+ IN DWORD RplUserRid
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ NET_API_STATUS status;
+
+ RpcTryExcept {
+ status = NetrRplSetSecurity(
+ (RPL_RPC_HANDLE)ServerHandle,
+ WkstaName,
+ WkstaRid,
+ RplUserRid
+ );
+ }
+ RpcExcept( EXCEPTION_EXECUTE_HANDLER) {
+ status = RplMapRpcError( RpcExceptionCode(), ERROR_INVALID_HANDLE);
+ }
+ RpcEndExcept
+
+ return( status);
+}
+
+
+DWORD RplMapRpcError(
+ IN DWORD RpcError,
+ IN DWORD BadContextError
+ )
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful error
+ for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+ BadContextError - Supplies the error code to return whenever an error
+ which indicates invalid context is received. In some cases, this
+ value is ERROR_INVALID_HANDLE in others, it is ERROR_INVALID_SERVICE_LOCK.
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+ switch ( RpcError) {
+
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return( BadContextError);
+
+ case RPC_X_NULL_REF_POINTER:
+ return( ERROR_INVALID_PARAMETER);
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ return( ERROR_INVALID_ADDRESS);
+
+ default:
+ return( RpcError);
+ }
+}
+
diff --git a/private/net/svcdlls/rpl/client/sources b/private/net/svcdlls/rpl/client/sources
new file mode 100644
index 000000000..6227f5162
--- /dev/null
+++ b/private/net/svcdlls/rpl/client/sources
@@ -0,0 +1,62 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27 - July - 1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = services
+TARGETNAME= rplsvc
+
+
+NTPROFILEINPUT=YES
+
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+TARGETLIBS=
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES=rplstub.c \
+ rplbind.c \
+ rplsvc_c.c
+
+
+UMTYPE= console
+!IF 0
+UMAPPL= rplapi
+!ELSE
+UMTEST= rplapi
+!ENDIF
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
diff --git a/private/net/svcdlls/rpl/command/makefile b/private/net/svcdlls/rpl/command/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/command/makefile.inc b/private/net/svcdlls/rpl/command/makefile.inc
new file mode 100644
index 000000000..803916b39
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/makefile.inc
@@ -0,0 +1,4 @@
+rplmsg.rc: msg00001.bin
+
+rplmsg.h msg00001.bin: rplmsg.mc
+ mc -v rplmsg.mc
diff --git a/private/net/svcdlls/rpl/command/rplcmd.c b/private/net/svcdlls/rpl/command/rplcmd.c
new file mode 100644
index 000000000..b5f2b32e9
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplcmd.c
@@ -0,0 +1,2408 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplcmd.c
+
+Abstract:
+
+ This file contains program for RemoteBoot command program.
+ This program originated from rplapi.c in ..\client directory.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 19-November-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint prototype
+#include <ntrtl.h> // DbgPrint prototype
+#include <nturtl.h> // Needed by winbase.h
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+
+#include <lmcons.h> // NET_API_STATUS
+#include <lmerr.h> // NetError codes
+#include <lmrpl.h> // RPL_HANDLE
+#include <i_lmrpl.h> // RPL_CONFIG_INFO_1
+#include <lmapibuf.h> // NetApiBufferFree()
+#include <stdlib.h> // exit()
+#include <stdio.h> // printf
+#include <ctype.h> // toupper - bombs if I use it
+#include "jet.h" // JetError (for rpllib.h)
+
+#include "rplmsg.h" // private RPL command error codes & messages
+#include "rpllib.h" // RplPrintf
+
+#define RPL_BUFFER_GET_ALL ((DWORD)-1)
+
+#if DBG
+#define RPL_DEBUG
+#endif // DBG
+
+#ifdef RPL_DEBUG
+#define RplDbgPrint( _x_) printf _x_
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ RplDbgPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgPrint( "[RplApi] File %s, Line %d\n", __FILE__, __LINE__); \
+ DbgUserBreakPoint(); \
+ } \
+ }
+#else
+#define RplDbgPrint( _x_)
+#define RPL_ASSERT( condition)
+#endif
+
+#define QUESTION_SW L"/?"
+#define QUESTION_SW_TOO L"-?"
+#define RPL_GENERIC_ERROR 1
+
+RPL_HANDLE GlobalServerHandle;
+BOOL GlobalHaveServerHandle = FALSE;
+PWCHAR GlobalServerName;
+HANDLE GlobalMessageHandle;
+
+
+int FileIsConsole( int fh)
+{
+ unsigned htype ;
+
+ htype = GetFileType(GetStdHandle(fh));
+ htype &= ~FILE_TYPE_REMOTE;
+ return htype == FILE_TYPE_CHAR;
+}
+
+
+
+DWORD ConsolePrint(
+ LPWSTR pch,
+ int cch
+ )
+{
+ int cchOut;
+ BOOL Success;
+ CHAR *pchOemBuffer;
+
+ if ( cch == 0) {
+ return( 0);
+ }
+
+ if ( FileIsConsole(STD_OUTPUT_HANDLE)) {
+ Success = WriteConsole(
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ pch, cch, &cchOut, NULL);
+ if ( Success) {
+ return( cchOut);
+ }
+ }
+
+ cchOut = WideCharToMultiByte( CP_OEMCP, 0, pch, cch, NULL, 0, NULL,NULL);
+ if (cchOut == 0) {
+ return 0;
+ }
+
+ if ((pchOemBuffer = (CHAR *)malloc(cchOut)) != NULL) {
+ WideCharToMultiByte(CP_OEMCP, 0, pch, cch,
+ pchOemBuffer, cchOut, NULL, NULL);
+ WriteFile( GetStdHandle(STD_OUTPUT_HANDLE),
+ pchOemBuffer, cchOut, &cch, NULL);
+ free(pchOemBuffer);
+ }
+
+ return cchOut;
+}
+
+
+
+DWORD MessagePrint(
+ IN DWORD MessageId,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Finds the unicode message corresponding to the supplied message id,
+ merges it with caller supplied string(s), and prints the resulting
+ string.
+
+Arguments:
+
+ MessageId - message id
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ printed by this routine. Zero return value indicates failure.
+
+--*/
+{
+ va_list arglist;
+ WCHAR * buffer = NULL;
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER;
+
+ if ( MessageId == NO_ERROR) {
+ return( 0);
+ }
+
+ va_start( arglist, MessageId);
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else if ( MessageId >= IDS_LOAD_LIBRARY_FAILURE
+ && MessageId <= IDS_RPLSVC_LOCAL_COMPUTER ) {
+ //
+ // Get message from this module.
+ //
+ lpSource = NULL;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = GlobalMessageHandle;
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0L, // dwLanguageId
+ (LPTSTR)&buffer, // lpBuffer
+ 0, // size
+ &arglist // lpArguments
+ );
+
+ length = ConsolePrint(buffer, length);
+
+ LocalFree(buffer);
+
+ return( length);
+
+} // MessagePrint()
+
+
+
+BOOL ReadString(
+ IN DWORD MessageId,
+ OUT PWCHAR * pString,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ WCHAR WcharBuffer[ 300];
+ DWORD Length; // includes terminal null wchar
+
+ RplPrintf0( MessageId );
+
+ *pString = NULL;
+
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( *Line == 0) {
+ //
+ // Zero length input is OK only when input is optional (and in that
+ // case pointer is assumed to be NULL).
+ //
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ RplPrintfID( IDS_NOTSUPPLIED, MessageId );
+ return( FALSE);
+ }
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, Line,
+ -1, WcharBuffer, sizeof( WcharBuffer));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (PWCHAR)Line );
+ return( FALSE);
+ }
+ *pString = LocalAlloc( GMEM_FIXED, Length * sizeof(WCHAR));
+ if ( *pString == NULL) {
+ RplDbgPrint(( "LocalAlloc failed"));
+ return( FALSE);
+ }
+ wcscpy( *pString, WcharBuffer);
+ return( TRUE);
+}
+
+
+//
+// returns -1 on error, position of selection (starting from 0) otherwise
+//
+int ReadFromMenu(
+ IN DWORD MenuMessageId,
+ IN DWORD FilterMessageId
+ )
+{
+ int ReturnValue = -1;
+ WCHAR Char = L'\0';
+ PWCHAR InputBuffer = NULL;
+ PWCHAR FilterBuffer = NULL;
+ int pos;
+
+ RplSPrintfN( FilterMessageId, NULL, 0, &FilterBuffer );
+ if (FilterBuffer == NULL) {
+ // this should never happen
+ goto nomsg_cleanup;
+ }
+
+ if ( !ReadString( MenuMessageId, &InputBuffer, FALSE ) ) {
+ goto nomsg_cleanup;
+ }
+
+ if ( InputBuffer == NULL
+ || (*InputBuffer) == L'\0'
+ || !CharUpperBuff( InputBuffer, 1 ) ) {
+ goto cleanup;
+ }
+ Char = InputBuffer[0];
+
+ pos = 0;
+ while (FilterBuffer[pos] != L'\0') {
+ if (Char == FilterBuffer[pos]) {
+ break;
+ }
+ pos++;
+ }
+
+ if (FilterBuffer[pos] == L'\0') {
+ goto cleanup;
+ }
+
+ ReturnValue = pos;
+
+
+cleanup:
+ if ( ReturnValue == -1 ) {
+ if (InputBuffer == NULL) {
+ RplPrintf2( IDS_BADMENUSEL, L"", FilterBuffer);
+ } else {
+ if (InputBuffer[0] != L'\0') {
+ InputBuffer[1] = L'\0';
+ }
+ RplPrintf2( IDS_BADMENUSEL, InputBuffer, FilterBuffer);
+ }
+ }
+
+nomsg_cleanup:
+
+ if (FilterBuffer != NULL) {
+ LocalFree( FilterBuffer );
+ }
+ if (InputBuffer != NULL) {
+ LocalFree( InputBuffer );
+ }
+
+ return( ReturnValue);
+}
+
+
+BOOL ReadInt(
+ IN DWORD MessageId,
+ OUT int * pInt,
+ IN BOOL MustHaveInput
+ )
+{
+ BYTE Line[ 300];
+ RplPrintf0( MessageId );
+ if ( gets( Line) == NULL) {
+ return( FALSE);
+ }
+ if ( sscanf( Line, "%d", pInt) != 1) {
+ if ( MustHaveInput == FALSE) {
+ return( TRUE);
+ } else {
+ RplPrintfID( IDS_NOTSUPPLIED, MessageId );
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL ReadWkstaFlags(
+ OUT PDWORD pFlags
+ )
+{
+ *pFlags = 0;
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_LOGON_INPUT,
+ IDS_PROMPT_FILTER_WKSTA_LOGON_INPUT )) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_REQUIRED;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_OPTIONAL;
+ break;
+ case 2:
+ *pFlags |= WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_SHAREDORPRIVATE,
+ IDS_PROMPT_FILTER_WKSTA_SHAREDORPRIVATE )) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_SHARING_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_SHARING_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_DHCP,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_DHCP_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_DHCP_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_DELETE,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= WKSTA_FLAGS_DELETE_TRUE;
+ break;
+ case 1:
+ *pFlags |= WKSTA_FLAGS_DELETE_FALSE;
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+
+DWORD ConfigDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_CONFIGNAME_NOTAB, Info->ConfigName );
+ RplPrintf1( IDS_CONFIGCOMMENT, Info->ConfigComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & CONFIG_FLAGS_MASK_ENABLED) {
+ case CONFIG_FLAGS_ENABLED_TRUE:
+ RplPrintf0( IDS_CONFIGENABLED );
+ break;
+ case CONFIG_FLAGS_ENABLED_FALSE:
+ RplPrintf0( IDS_CONFIGDISABLED );
+ break;
+ default:
+ RplPrintf1( IDS_CONFIGBADFLAGS, (PWSTR)Info->Flags );
+ break;
+ }
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BOOTNAME, Info->BootName );
+ RplPrintf1( IDS_DIRNAME, Info->DirName );
+ RplPrintf1( IDS_DIRNAME2, Info->DirName2 );
+ RplPrintf1( IDS_DIRNAME3, Info->DirName3 );
+ RplPrintf1( IDS_DIRNAME4, Info->DirName4 );
+ RplPrintf1( IDS_FITSHARED, Info->FitShared );
+ RplPrintf1( IDS_FITPERSONAL, Info->FitPersonal );
+ return( NO_ERROR);
+}
+
+
+
+VOID TestConfigEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ printf( "\nConfigEnum: Level=%d", Level);
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ printf( ", PrefMaxLength=%ld", PrefMaxLength);
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( AdapterName != NULL) {
+ RplDbgPrint(( ", AdapterName=%ws", AdapterName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplConfigEnum( GlobalServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ConfigDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+#define PROFILE_FLAGS_DISK_PRESENT_TRUE ((DWORD)0x00000001)
+#define PROFILE_FLAGS_DISK_PRESENT_FALSE ((DWORD)0x00000002)
+#define PROFILE_FLAGS_MASK_DISK_PRESENT \
+ ( PROFILE_FLAGS_DISK_PRESENT_FALSE | \
+ PROFILE_FLAGS_DISK_PRESENT_TRUE )
+
+typedef enum _DISPLAY_ACTION {
+ DisplayActionSetInfo = 0,
+ DisplayActionGetInfo
+} DISPLAY_ACTION;
+
+
+DWORD ProfileDisplayInfo(
+ IN BOOL SetInfo,
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1(IDS_PROFILENAME_NOTAB, Info->ProfileName );
+ RplPrintf1(IDS_PROFILECOMMENT, Info->ProfileComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & PROFILE_FLAGS_MASK_DISK_PRESENT) {
+ case PROFILE_FLAGS_DISK_PRESENT_TRUE:
+ break; // do not report anything if all is good
+ case PROFILE_FLAGS_DISK_PRESENT_FALSE:
+ RplPrintf0( IDS_PROFILENODISKTREE );
+ break;
+ case 0:
+ //
+ // 0 is OK value for SetInfo, but not for Add & GetInfo
+ //
+ if ( SetInfo == TRUE) {
+ break;
+ }
+ default:
+ RplPrintf1( IDS_PROFILEBADFLAGS, (LPWSTR)Info->Flags);
+ break;
+ }
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_CONFIGNAME, Info->ConfigName );
+ RplPrintf1( IDS_BOOTNAME, Info->BootName );
+ RplPrintf1( IDS_FITSHARED, Info->FitShared );
+ RplPrintf1( IDS_FITPERSONAL, Info->FitPersonal );
+ return( NO_ERROR);
+}
+
+
+
+VOID TestProfileEnum(
+ IN DWORD Level,
+ IN PWCHAR AdapterName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nProfileEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( AdapterName != NULL) {
+ RplDbgPrint(( ", AdapterName=%ws", AdapterName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplProfileEnum( GlobalServerHandle, AdapterName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ ProfileDisplayInfo( FALSE, Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+DWORD ServiceDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_INFO_0 Info = Buffer;
+
+ if ( Level > 0) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_SERVICEBADFLAGS, (PWCHAR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD WkstaDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_WKSTANAME_NOTAB, Info->WkstaName );
+ RplPrintf1( IDS_WKSTACOMMENT, Info->WkstaComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ RplPrintf0( IDS_WKSTALOGONREQD );
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ RplPrintf0( IDS_WKSTALOGONOPT );
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ RplPrintf0( IDS_WKSTALOGONNONE );
+ break;
+ default:
+ RplPrintf1( IDS_WKSTALOGONBADFLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_SHARING) {
+ case WKSTA_FLAGS_SHARING_TRUE:
+ RplPrintf0( IDS_WKSTASHARINGTRUE );
+ break;
+ case WKSTA_FLAGS_SHARING_FALSE:
+ RplPrintf0( IDS_WKSTASHARINGFALSE );
+ break;
+ default:
+ RplPrintf1( IDS_WKSTASHARINGBADFLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DHCP) {
+ case WKSTA_FLAGS_DHCP_TRUE:
+ RplPrintf0( IDS_WKSTA_DHCP_TRUE);
+ break;
+ case WKSTA_FLAGS_DHCP_FALSE:
+ RplPrintf0( IDS_WKSTA_DHCP_FALSE);
+ break;
+ default:
+ RplPrintf1( IDS_WKSTA_DHCP_BAD_FLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DELETE) {
+ case WKSTA_FLAGS_DELETE_TRUE:
+ RplPrintf0( IDS_WKSTA_DELETE_TRUE);
+ break;
+ case WKSTA_FLAGS_DELETE_FALSE:
+ RplPrintf0( IDS_WKSTA_DELETE_FALSE);
+ break;
+ default:
+ RplPrintf1( IDS_WKSTA_DELETE_BAD_FLAGS, (LPWSTR)Info->Flags );
+ break;
+ }
+ RplPrintf1( IDS_WKSTAINPROFILE, Info->ProfileName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BOOTNAME, Info->BootName);
+ RplPrintf1( IDS_FITFILE, Info->FitFile);
+ RplPrintf1( IDS_WKSTAADAPTER, Info->AdapterName);
+ RplPrintf1( IDS_TCPIPADDRESS, (LPWSTR)Info->TcpIpAddress);
+ RplPrintf1( IDS_TCPIPSUBNET, (LPWSTR)Info->TcpIpSubnet);
+ RplPrintf1( IDS_TCPIPGATEWAY, (LPWSTR)Info->TcpIpGateway);
+ return( NO_ERROR);
+}
+
+
+
+VOID TestWkstaEnum(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( ProfileName != NULL) {
+ RplDbgPrint(( ", ProfileName=%ws", ProfileName));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplWkstaEnum( GlobalServerHandle, ProfileName, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ WkstaDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+DWORD VendorDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_VENDORNAME_NOTAB, Info->VendorName );
+ RplPrintf1( IDS_VENDORCOMMENT, Info->VendorComment );
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_VENDORBADFLAGS, (LPWSTR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD BootDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ if ( Level > 2) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_BOOTNAME_NOTAB, Info->BootName);
+ RplPrintf1( IDS_BOOTCOMMENT, Info->BootComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags & BOOT_FLAGS_MASK_FINAL_ACKNOWLEDGMENT) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ RplPrintf0( IDS_BOOTACK_TRUE );
+ break;
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ RplPrintf0( IDS_BOOTACK_FALSE );
+ break;
+ default:
+ RplPrintf1( IDS_BOOTBADFLAGS, (LPWSTR)Info->Flags);
+ break;
+ }
+ RplPrintf1( IDS_VENDORNAME, Info->VendorName);
+ if ( Level == 1) {
+ return( NO_ERROR);
+ }
+ RplPrintf1( IDS_BBCFILE, Info->BbcFile);
+ RplPrintf1( IDS_WINDOWSIZE, (LPWSTR)Info->WindowSize);
+ return( NO_ERROR);
+}
+
+
+
+DWORD AdapterDisplayInfo(
+ IN DWORD Level,
+ OUT LPVOID Buffer
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ if ( Level > 1) {
+ return( ERROR_INVALID_LEVEL);
+ }
+ RplPrintf1( IDS_ADAPTERNAME_NOTAB, Info->AdapterName);
+ RplPrintf1( IDS_ADAPTERCOMMENT, Info->AdapterComment);
+ if ( Level == 0) {
+ return( NO_ERROR);
+ }
+ switch( Info->Flags) {
+ case 0:
+ break;
+ default:
+ RplPrintf1( IDS_ADAPTERBADFLAGS, (LPWSTR)Info->Flags);
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID TestAdapterEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nAdapterEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplAdapterEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ AdapterDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID TestAdapterAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestAdapterAdd\n"));
+ AdapterDisplayInfo( 1, Buffer);
+#endif
+ Error = NetRplAdapterAdd( GlobalServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID AdapterAdd( VOID)
+{
+ RPL_ADAPTER_INFO_1 Info;
+
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_ADAPTERCOMMENT, &Info.AdapterComment, FALSE)) {
+ return;
+ }
+ TestAdapterAdd( &Info);
+}
+
+
+VOID TestBootAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestBootAdd\n"));
+ BootDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplBootAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID BootAdd( VOID)
+{
+ RPL_BOOT_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &Info.VendorName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BBCFILE, &Info.BbcFile, TRUE)) {
+ return;
+ }
+ Info.Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_BOOTCOMMENT, &Info.BootComment, FALSE)) {
+ return;
+ }
+ Info.WindowSize = 0;
+ if ( !ReadInt( IDS_PROMPT_WINDOWSIZE, &Info.WindowSize, FALSE)) {
+ return;
+ }
+ TestBootAdd( &Info);
+}
+
+
+VOID BootDel( VOID)
+{
+ PWCHAR BootName;
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &VendorName, TRUE)) {
+ return;
+ }
+ Error = NetRplBootDel( GlobalServerHandle, BootName, VendorName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID TestBootEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_BOOT_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_BOOT_INFO_1);
+ break;
+ case 2:
+ CoreSize = sizeof( RPL_BOOT_INFO_2);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestBootEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplBootEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ BootDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID BootEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestBootEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestBootEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestConfigAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestConfigAdd\n"));
+ ConfigDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplConfigAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID ConfigAdd( VOID)
+{
+ RPL_CONFIG_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME, &Info.DirName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME2, &Info.DirName2, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_CONFIGCOMMENT, &Info.ConfigComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME3, &Info.DirName3, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_DIRNAME4, &Info.DirName4, FALSE)) {
+ return;
+ }
+ TestConfigAdd( &Info);
+}
+
+
+VOID ConfigDel( VOID)
+{
+ PWCHAR ConfigName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &ConfigName, FALSE)) {
+ return;
+ }
+ Error = NetRplConfigDel( GlobalServerHandle, ConfigName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID TestVendorAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestVendorAdd\n"));
+ VendorDisplayInfo( 1, Buffer);
+#endif
+ Error = NetRplVendorAdd( GlobalServerHandle, 1, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID VendorAdd( VOID)
+{
+ RPL_VENDOR_INFO_1 Info;
+
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &Info.VendorName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_VENDORCOMMENT, &Info.VendorComment, FALSE)) {
+ return;
+ }
+ TestVendorAdd( &Info);
+}
+
+
+VOID VendorDel( VOID)
+{
+ PWCHAR VendorName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_VENDORNAME, &VendorName, FALSE)) {
+ return;
+ }
+ Error = NetRplVendorDel( GlobalServerHandle, VendorName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID TestVendorEnum(
+ IN DWORD Level,
+ IN DWORD PrefMaxLength,
+ IN PDWORD pResumeHandle
+ )
+{
+ LPBYTE Buffer;
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ DWORD CoreSize;
+ DWORD index;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ break;
+ case 1:
+ CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ break;
+ default:
+ return;
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestVendorEnum: Level=%d", Level));
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ RplDbgPrint(( ", PrefMaxLength=%ld", PrefMaxLength));
+ } else {
+ RplDbgPrint(( ", unlimited buffer size"));
+ }
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle=0x%x\n\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(( ", not resumable.\n\n"));
+ }
+#endif
+
+ for ( ; ; ) {
+ Error = NetRplVendorEnum( GlobalServerHandle, Level, &Buffer,
+ PrefMaxLength, &EntriesRead, &TotalEntries, pResumeHandle);
+
+ if ( Error != NO_ERROR && Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ break;
+ }
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "Buffer = 0x%x, EntriesRead = %d, TotalEntries = %d", Buffer,
+ EntriesRead, TotalEntries));
+ if ( pResumeHandle != NULL) {
+ RplDbgPrint(( ", ResumeHandle = 0x%x\n", *pResumeHandle));
+ } else {
+ RplDbgPrint(("\n"));
+ }
+#endif
+
+ for ( index = 0; index < EntriesRead; index++) {
+ VendorDisplayInfo( Level, Buffer + index * CoreSize);
+ }
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+
+ if ( pResumeHandle == NULL) {
+ break;
+ }
+ if ( *pResumeHandle == 0) {
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ }
+ break;
+ }
+ if ( Error != ERROR_MORE_DATA) {
+ MessagePrint( Error);
+ MessagePrint( IDS_UNEXPECTED_RETURN_CODE, Error);
+ break;
+ }
+ }
+}
+
+
+VOID VendorEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL01, &Level, TRUE) || Level > 1) {
+ return;
+ }
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ Level = 0;
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestVendorEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestVendorEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID TestProfileAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestProfileAdd\n"));
+ ProfileDisplayInfo( FALSE, 2, Buffer);
+#endif
+ Error = NetRplProfileAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestProfileGetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplProfileGetInfo( GlobalServerHandle, ProfileName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ ProfileDisplayInfo( FALSE, Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestProfileSetInfo(
+ IN DWORD Level,
+ IN PWCHAR ProfileName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestProfileSetInfo: Level=%d, ProfileName=%ws\n", Level, ProfileName));
+ ProfileDisplayInfo( TRUE, Level, Buffer);
+#endif
+ Error = NetRplProfileSetInfo( GlobalServerHandle, ProfileName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestWkstaAdd( IN LPVOID Buffer)
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nTestWkstaAdd\n"));
+ WkstaDisplayInfo( 2, Buffer);
+#endif
+ Error = NetRplWkstaAdd( GlobalServerHandle, 2, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID TestWkstaGetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName
+ )
+{
+ LPBYTE Buffer;
+ DWORD Error;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaGetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName));
+#endif
+ Error = NetRplWkstaGetInfo( GlobalServerHandle, WkstaName, Level, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ WkstaDisplayInfo( Level, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID TestWkstaSetInfo(
+ IN DWORD Level,
+ IN PWCHAR WkstaName,
+ IN LPVOID Buffer
+ )
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+
+#ifdef RPL_DEBUG
+ RplDbgPrint(( "\nWkstaSetInfo: Level=%d, WkstaName=%ws\n", Level, WkstaName));
+ WkstaDisplayInfo( Level, Buffer);
+#endif
+ Error = NetRplWkstaSetInfo( GlobalServerHandle, WkstaName, Level, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID AdapterDel( VOID)
+{
+ PWCHAR AdapterName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( AdapterName == NULL) {
+ switch ( ReadFromMenu( IDS_PROMPT_MENU_ADAPTERDELETEALL,
+ IDS_PROMPT_FILTER_YN) ) {
+ case 0:
+ break;
+ case 1:
+ default:
+ return;
+ }
+ }
+
+ Error = NetRplAdapterDel( GlobalServerHandle, AdapterName);
+ if ( Error != 0) {
+ MessagePrint( Error);
+ }
+}
+
+
+VOID AdapterEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL01, &Level, TRUE) || Level > 1) {
+ return;
+ }
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ Level = 0;
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestAdapterEnum( Level, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestAdapterEnum( Level, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ConfigEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_ADAPTERNAME_COMPATCONFIG, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestConfigEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestConfigEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileAdd( VOID)
+{
+ RPL_PROFILE_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, TRUE)) {
+ return;
+ }
+ Info.Flags = 0;
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT, &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, FALSE)) {
+ return;
+ }
+ TestProfileAdd( &Info);
+}
+
+
+VOID ProfileClone( VOID)
+{
+ PWCHAR SourceProfileName;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetProfileComment;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_SOURCE, &SourceProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_TARGET, &TargetProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT_TARGET, &TargetProfileComment, FALSE)) {
+ return;
+ }
+ Error = NetRplProfileClone( GlobalServerHandle, SourceProfileName,
+ TargetProfileName, TargetProfileComment);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID ProfileDel( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &ProfileName, TRUE)) {
+ return;
+ }
+ Error = NetRplProfileDel( GlobalServerHandle, ProfileName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID ProfileEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR AdapterName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_ADAPTERNAME_COMPATPROF, &AdapterName, FALSE)) {
+ return;
+ }
+ if ( PrefMaxLength != RPL_BUFFER_GET_ALL) {
+ ResumeHandle = 0, TestProfileEnum( Level, AdapterName, PrefMaxLength, &ResumeHandle);
+ } else {
+ TestProfileEnum( Level, AdapterName, RPL_BUFFER_GET_ALL, NULL);
+ }
+}
+
+
+VOID ProfileGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR ProfileName[ 20];
+ CHAR ProfileNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ RplPrintf0( IDS_PROMPT_PROFILENAME_INFOLEVEL );
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, ProfileNameA);
+ if ( Count != 2) {
+ RplPrintf0( IDS_BADARGCOUNT );
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, ProfileNameA, -1,
+ ProfileName, sizeof( ProfileName));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (LPWSTR)ProfileNameA);
+ return;
+ }
+ TestProfileGetInfo( Level, ProfileName);
+}
+
+
+VOID ProfileSetInfo( VOID)
+{
+ PWCHAR ProfileName;
+ DWORD Level;
+ RPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+
+ Buffer = &Info;
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME_INPUT, &ProfileName, TRUE)) {
+ return;
+ }
+ RplPrintf0( IDS_INPUTPROFILEPROPERTIES );
+ Info.ProfileName = NULL;
+ // ReadString( "ProfileName", &Info.ProfileName);
+ if ( !ReadString( IDS_PROMPT_PROFILECOMMENT, &Info.ProfileComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ Info.Flags = 0;
+ if ( Level == 1) {
+ goto testit;
+ }
+ Info.ConfigName = NULL;
+ // ReadString( IDS_PROMPT_CONFIGNAME, &Info.ConfigName, FALSE)) {
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITSHARED, &Info.FitShared, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITPERSONAL, &Info.FitPersonal, FALSE)) {
+ return;
+ }
+testit:
+ TestProfileSetInfo( Level, ProfileName, Buffer);
+}
+
+
+VOID ServiceClose( VOID)
+{
+ DWORD Error;
+
+ if ( GlobalHaveServerHandle == FALSE) {
+ RplPrintf0( IDS_NOSERVICEHANDLE );
+ return;
+ }
+ Error = NetRplClose( GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ LocalFree( GlobalServerName); // OK if NULL pointer
+ GlobalHaveServerHandle = FALSE;
+}
+
+
+VOID ServiceGetInfo( VOID)
+{
+ LPBYTE Buffer;
+ DWORD Error;
+ Error = NetRplGetInfo( GlobalServerHandle, 0, &Buffer);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ if ( GlobalServerName == NULL) {
+ RplPrintf0( IDS_RPLSVC_LOCAL_COMPUTER);
+ } else {
+ RplPrintf1( IDS_RPLSVC_COMPUTER, GlobalServerName);
+ }
+ ServiceDisplayInfo( 0, Buffer);
+ NetApiBufferFree( Buffer); // =~ MIDL_user_free()
+}
+
+
+VOID ServiceOpen( VOID)
+{
+ DWORD Error;
+
+ if ( GlobalHaveServerHandle == TRUE) {
+ RplPrintf0( IDS_MUSTCLOSEHANDLE );
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_SERVERNAME, &GlobalServerName, FALSE)) {
+ return;
+ }
+ Error = NetRplOpen( GlobalServerName, &GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+ GlobalHaveServerHandle = TRUE;
+}
+
+
+BOOL ReadServiceFlags(
+ OUT PDWORD pFlags
+ )
+{
+ *pFlags = 0;
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_SECURITY,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_CHECK_SECURITY;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_CONFIGS,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_CHECK_CONFIGS;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+#ifdef NOT_YET
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_RPLDISK,
+ IDS_PROMPT_FILTER_SERVICE_RPLDISK )) {
+ case 0:
+ *pFlags |= RPL_REPLACE_RPLDISK;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+#endif
+
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_BACKUP,
+ IDS_PROMPT_FILTER_YN)) {
+ case 0:
+ *pFlags |= RPL_BACKUP_DATABASE;
+ break;
+ case 1:
+ break;
+ default:
+ // this really shouldn't happen; fall through
+ case -1:
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+
+VOID ServiceSetInfo( VOID)
+{
+ DWORD Level;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+
+ Level = 0;
+
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_LEVEL_DEBUG, &Level, FALSE)) {
+ return;
+ }
+#endif
+
+ switch( Level) {
+ case 0: {
+ RPL_INFO_0 Info;
+ Buffer = &Info;
+ if ( !ReadServiceFlags( &Info.Flags)) {
+ return;
+ }
+ break;
+ }
+ default:
+ return;
+ break;
+ }
+ Error = NetRplSetInfo( GlobalServerHandle, 0, Buffer, &ErrorParameter);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ RplDbgPrint(( "ErrorParameter = %d\n", ErrorParameter));
+ return;
+ }
+}
+
+
+VOID WkstaAdd( VOID)
+{
+ RPL_WKSTA_INFO_2 Info;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME, &Info.WkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, TRUE)) {
+ return;
+ }
+ if ( !ReadWkstaFlags( &Info.Flags)) {
+ return;
+ }
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_WKSTACOMMENT, &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITFILE, &Info.FitFile, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPSUBNET, &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPGATEWAY, &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+ TestWkstaAdd( &Info);
+}
+
+
+VOID WkstaClone( VOID)
+{
+ PWCHAR SourceWkstaName;
+ PWCHAR TargetWkstaName;
+ PWCHAR TargetWkstaComment;
+ PWCHAR TargetAdapterName;
+ DWORD TargetTcpIpAddress;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_SOURCE, &SourceWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_TARGET, &TargetWkstaName, TRUE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME_TARGET, &TargetAdapterName, TRUE)) {
+ return;
+ }
+ RplPrintf0( IDS_PARAMETERSOPTIONAL );
+ if ( !ReadString( IDS_PROMPT_WKSTACOMMENT_TARGET, &TargetWkstaComment, FALSE)) {
+ return;
+ }
+ TargetTcpIpAddress = (DWORD)-1;
+ if( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &TargetTcpIpAddress, FALSE)) {
+ return;
+ }
+ Error = NetRplWkstaClone( GlobalServerHandle, SourceWkstaName,
+ TargetWkstaName, TargetWkstaComment, TargetAdapterName, TargetTcpIpAddress);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID WkstaDel( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Error;
+
+ if ( !ReadString( IDS_PROMPT_WKSTANAME, &WkstaName, TRUE)) {
+ return;
+ }
+ Error = NetRplWkstaDel( GlobalServerHandle, WkstaName);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ return;
+ }
+}
+
+
+VOID WkstaEnum( VOID)
+{
+ DWORD Level;
+ DWORD PrefMaxLength;
+ DWORD ResumeHandle;
+ PWCHAR ProfileName;
+
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+#ifdef RPL_DEBUG
+ if ( !ReadInt( IDS_PROMPT_PREFMAXLENGTH_DEBUG, &PrefMaxLength, TRUE)) {
+ return;
+ }
+#else
+ PrefMaxLength = RPL_BUFFER_GET_ALL;
+#endif
+ if( !ReadString( IDS_PROMPT_PROFILENAME_COMPATWKSTA, &ProfileName, FALSE)) {
+ return;
+ }
+ ResumeHandle = 0, TestWkstaEnum( Level, ProfileName, PrefMaxLength, &ResumeHandle);
+}
+
+
+VOID WkstaGetInfo( VOID)
+{
+ BYTE Line[ 300];
+ WCHAR WkstaName[ 20];
+ CHAR WkstaNameA[ 20];
+ DWORD Length;
+ DWORD Count;
+ DWORD Level;
+
+ RplPrintf0( IDS_PROMPT_WKSTANAME_INFOLEVEL );
+ if ( gets( Line) == NULL) {
+ return;
+ }
+ Count = sscanf( Line, "%d %s", &Level, WkstaNameA);
+ if ( Count != 2) {
+ RplPrintf0( IDS_BADARGCOUNT );
+ return;
+ }
+ Length = MultiByteToWideChar( CP_OEMCP, MB_PRECOMPOSED, WkstaNameA, -1,
+ WkstaName, sizeof( WkstaName));
+ if ( Length == 0) {
+ RplPrintf1( IDS_INVALIDSTRING_CHARSTRINGA, (LPWSTR)WkstaNameA);
+ return;
+ }
+ TestWkstaGetInfo( Level, WkstaName);
+}
+
+
+VOID WkstaSetInfo( VOID)
+{
+ PWCHAR WkstaName;
+ DWORD Level;
+ LPVOID Buffer;
+ RPL_WKSTA_INFO_2 Info;
+
+ Buffer = &Info;
+ if ( !ReadInt( IDS_PROMPT_LEVEL012, &Level, TRUE) || Level > 2) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_WKSTANAME_INPUT, &WkstaName, FALSE)) {
+ return;
+ }
+ RplPrintf0( IDS_INPUTWKSTAPROPERTIES );
+ if( !ReadString( IDS_PROMPT_WKSTANAME, &Info.WkstaName, FALSE)) {
+ return;
+ }
+ if( !ReadString( IDS_PROMPT_WKSTACOMMENT, &Info.WkstaComment, FALSE)) {
+ return;
+ }
+ if ( Level == 0) {
+ goto testit;
+ }
+ if ( !ReadWkstaFlags( &Info.Flags)) {
+ return;
+ }
+ if( !ReadString( IDS_PROMPT_PROFILENAME, &Info.ProfileName, FALSE)) {
+ return;
+ }
+ if ( Level == 1) {
+ goto testit;
+ }
+ if ( !ReadString( IDS_PROMPT_BOOTNAME, &Info.BootName, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_FITFILE, &Info.FitFile, FALSE)) {
+ return;
+ }
+ if ( !ReadString( IDS_PROMPT_ADAPTERNAME, &Info.AdapterName, FALSE)) {
+ return;
+ }
+ Info.TcpIpAddress = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPADDRESS, &Info.TcpIpAddress, FALSE)) {
+ return;
+ }
+ Info.TcpIpSubnet = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPSUBNET, &Info.TcpIpSubnet, FALSE)) {
+ return;
+ }
+ Info.TcpIpGateway = (DWORD)-1;
+ if ( !ReadInt( IDS_PROMPT_TCPIPGATEWAY, &Info.TcpIpGateway, FALSE)) {
+ return;
+ }
+testit:
+ TestWkstaSetInfo( Level, WkstaName, Buffer);
+}
+
+//
+// BUGBUG INTL: still must deal with char constants
+//
+
+
+VOID Worker( VOID)
+{
+ for ( ; ;) {
+ switch( ReadFromMenu( IDS_PROMPT_MENU_MAIN_ABCPSVWQ,
+ IDS_PROMPT_FILTER_MAIN_ABCPSVWQ )) {
+ case 0:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_ADAPTER_ADE,
+ IDS_PROMPT_FILTER_ADAPTER_ADE )) {
+ case 0:
+ AdapterAdd();
+ break;
+ case 1:
+ AdapterDel();
+ break;
+ case 2:
+ AdapterEnum();
+ break;
+ }
+ break;
+ case 1:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_BOOT_ADE,
+ IDS_PROMPT_FILTER_BOOT_ADE )) {
+ case 0:
+ BootAdd();
+ break;
+ case 1:
+ BootDel();
+ break;
+ case 2:
+ BootEnum();
+ break;
+ }
+ break;
+ case 2:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_CONFIG_ADE,
+ IDS_PROMPT_FILTER_CONFIG_ADE )) {
+ case 0:
+ ConfigAdd();
+ break;
+ case 1:
+ ConfigDel();
+ break;
+ case 2:
+ ConfigEnum();
+ break;
+ }
+ break;
+ case 3:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_PROFILE_ACDEGS,
+ IDS_PROMPT_FILTER_PROFILE_ACDEGS )) {
+ case 0:
+ ProfileAdd();
+ break;
+ case 1:
+ ProfileClone();
+ break;
+ case 2:
+ ProfileDel();
+ break;
+ case 3:
+ ProfileEnum();
+ break;
+ case 4:
+ ProfileGetInfo();
+ break;
+ case 5:
+ ProfileSetInfo();
+ break;
+ }
+ break;
+ case 4:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_SERVICE_CGOS,
+ IDS_PROMPT_FILTER_SERVICE_CGOS )) {
+ case 0:
+ ServiceClose();
+ break;
+ case 1:
+ ServiceGetInfo();
+ break;
+ case 2:
+ ServiceOpen();
+ break;
+ case 3:
+ ServiceSetInfo();
+ break;
+ }
+ break;
+ case 5:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_VENDOR_ADE,
+ IDS_PROMPT_FILTER_VENDOR_ADE )) {
+ case 0:
+ VendorAdd();
+ break;
+ case 1:
+ VendorDel();
+ break;
+ case 2:
+ VendorEnum();
+ break;
+ }
+ break;
+ case 6:
+ switch( ReadFromMenu( IDS_PROMPT_MENU_WKSTA_ACDEGS,
+ IDS_PROMPT_FILTER_WKSTA_ACDEGS )) {
+ case 0:
+ WkstaAdd();
+ break;
+ case 1:
+ WkstaClone();
+ break;
+ case 2:
+ WkstaDel();
+ break;
+ case 3:
+ WkstaEnum();
+ break;
+ case 4:
+ WkstaGetInfo();
+ break;
+ case 5:
+ WkstaSetInfo();
+ break;
+ }
+ break;
+ case 7:
+ return;
+ break;
+ default:
+ // go back to start of loop
+ break;
+ }
+ }
+}
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ DWORD Error;
+ DWORD Length;
+
+ GlobalMessageHandle = LoadLibrary( L"netmsg.dll");
+ if ( GlobalMessageHandle == NULL) {
+ MessagePrint( IDS_LOAD_LIBRARY_FAILURE, GetLastError());
+ goto ErrorExit;
+ }
+
+ if ( argc == 1) {
+ GlobalServerName = NULL;
+ } else if ( argc == 2) {
+ Length = (MAX_PATH+1+2) * sizeof(WCHAR); // terminal null + two backslashes
+ GlobalServerName = LocalAlloc( GMEM_FIXED, Length);
+ if ( GlobalServerName == NULL) {
+ RplDbgPrint(( "LocalAlloc failed"));
+ goto ErrorExit;
+ }
+ Length = MultiByteToWideChar(
+ CP_OEMCP,
+ MB_PRECOMPOSED,
+ argv[ 1],
+ -1,
+ GlobalServerName,
+ Length
+ );
+ if ( Length == 0 || !_wcsicmp( GlobalServerName, QUESTION_SW)
+ || !_wcsicmp( GlobalServerName, QUESTION_SW_TOO)) {
+ goto ErrorExit;
+ }
+#ifdef RPL_DEBUG
+ RplPrintf1( IDS_TARGETSERVER, GlobalServerName);
+#endif
+ } else {
+ goto ErrorExit;
+ }
+
+ Error = NetRplOpen( GlobalServerName, &GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ MessagePrint( IDS_OPEN_SERVICE_HANDLE);
+ goto ErrorExit;
+ }
+ GlobalHaveServerHandle = TRUE;
+
+ Worker();
+
+ if ( GlobalHaveServerHandle == FALSE) {
+ return(0);
+ }
+
+ Error = NetRplClose( GlobalServerHandle);
+ if ( Error != NO_ERROR) {
+ MessagePrint( Error);
+ MessagePrint( IDS_CLOSE_SERVICE_HANDLE);
+ goto ErrorExit;
+ }
+ GlobalHaveServerHandle = FALSE; // in case one adds code below
+ return(0);
+
+ErrorExit:
+
+ RplPrintf0( IDS_USAGE );
+ return(1);
+}
+
diff --git a/private/net/svcdlls/rpl/command/rplmsg.mc b/private/net/svcdlls/rpl/command/rplmsg.mc
new file mode 100644
index 000000000..b2c8d526b
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplmsg.mc
@@ -0,0 +1,610 @@
+;/*++
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; rplmsg.h
+;
+;Abstract:
+;
+; Definitions for Remoteboot command private errors.
+;
+;Author:
+;
+; Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+;
+;Revision History:
+; Jon Newman April - 28 - 1994
+; moved & cleaned up bunch of messages
+;
+;
+;Notes:
+;
+; This file is generated by the MC tool from the rplmsg.mc file.
+;
+;
+;--*/
+
+MessageId=10000 SymbolicName=IDS_LOAD_LIBRARY_FAILURE
+Language=English
+LoadLibrary() fails with winError = %1!d!%n%0
+.
+MessageId=+1 SymbolicName=IDS_UNEXPECTED_RETURN_CODE
+Language=English
+Remote boot server returned an unexpected error = %1!d!%n%0
+.
+MessageId=+1 SymbolicName=IDS_OPEN_SERVICE_HANDLE
+Language=English
+Failed to open remote boot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CLOSE_SERVICE_HANDLE
+Language=English
+Failed to close remote boot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERCOMMENT
+Language=English
+AdapterComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME
+Language=English
+AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_COMPATCONFIG
+Language=English
+To list only configurations compatible with a given network card, input AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_COMPATPROF
+Language=English
+To list only profiles compatible with a given network card, input AdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_ADAPTERNAME_TARGET
+Language=English
+TargetAdapterName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BOOTCOMMENT
+Language=English
+BootComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BOOTNAME
+Language=English
+BootName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_CONFIGCOMMENT
+Language=English
+ConfigComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_CONFIGNAME
+Language=English
+ConfigName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME
+Language=English
+DirName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME2
+Language=English
+DirName2=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME3
+Language=English
+DirName3=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_DIRNAME4
+Language=English
+DirName4=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITFILE
+Language=English
+FitFile=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITPERSONAL
+Language=English
+FitPersonal=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FITSHARED
+Language=English
+FitShared=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL_DEBUG
+Language=English
+Information Level=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL01
+Language=English
+Information Level (0 or 1)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_LEVEL012
+Language=English
+Information Level (0, 1 or 2)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTER_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_ADAPTER_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTER_DE
+Language=English
+Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_ADAPTERDELETEALL
+Language=English
+Are you sure you wish to delete all adapters? (Y/N)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_BOOT_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_BOOT_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_CONFIG_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_CONFIG_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_MAIN_ABCPSVWQ
+Language=English
+Adapter Boot Config Profile Service Vendor Wksta [Quit]: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_MAIN_ABCPSVWQ
+Language=English
+ABCPSVWQ%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_PROFILE_ACDEGS
+Language=English
+Add Clone Del Enum GetInfo SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_PROFILE_ACDEGS
+Language=English
+ACDEGS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_CGOS
+Language=English
+Close GetInfo Open SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_SERVICE_CGOS
+Language=English
+CGOS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_CONFIGS
+Language=English
+Would you like to check for installed configurations [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_RPLDISK
+Language=English
+Would you like to update client boot files [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_BACKUP
+Language=English
+Would you backup service database [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_YN
+Language=English
+YN%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_SERVICE_SECURITY
+Language=English
+Would you check logon domain for remoteboot workstations [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_VENDOR_ADE
+Language=English
+Add Del Enum: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_VENDOR_ADE
+Language=English
+ADE%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_ACDEGS
+Language=English
+Add Clone Del Enum GetInfo SetInfo: %0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_ACDEGS
+Language=English
+ACDEGS%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_DELETE
+Language=English
+Delete corresponding user account when workstation is deleted by Remoteboot Manager [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_DHCP
+Language=English
+Does RemoteBoot workstation use DHCP [Y,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_LOGON_INPUT
+Language=English
+Is Remoteboot password Required, Optional or Not solicited [R,O,N]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_LOGON_INPUT
+Language=English
+RON%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_MENU_WKSTA_SHAREDORPRIVATE
+Language=English
+Does RemoteBoot workstation uses shared or private home directory [S,P]=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_FILTER_WKSTA_SHAREDORPRIVATE
+Language=English
+SP%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PREFMAXLENGTH_DEBUG
+Language=English
+PrefMaxLength (-1 for all data)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILECOMMENT
+Language=English
+ProfileComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILECOMMENT_TARGET
+Language=English
+TargetProfileComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME
+Language=English
+ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_COMPATWKSTA
+Language=English
+To list only workstations using a given profile, input ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_INFOLEVEL
+Language=English
+Input Information Level (0, 1 or 2) and ProfileName%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_INPUT
+Language=English
+Input ProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_SOURCE
+Language=English
+SourceProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_PROFILENAME_TARGET
+Language=English
+TargetProfileName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_SERVERNAME
+Language=English
+ServerName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPADDRESS
+Language=English
+TcpIpAddress (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPGATEWAY
+Language=English
+TcpIpGateway (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_TCPIPSUBNET
+Language=English
+TcpIpSubnet (when DHCP is disabled)=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_VENDORCOMMENT
+Language=English
+VendorComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_VENDORNAME
+Language=English
+VendorName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_BBCFILE
+Language=English
+BbcFile=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WINDOWSIZE
+Language=English
+WindowSize=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTACOMMENT
+Language=English
+WkstaComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTACOMMENT_TARGET
+Language=English
+TargetWkstaComment=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME
+Language=English
+WkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_INFOLEVEL
+Language=English
+Input Information Level (0, 1 or 2) and WkstaName%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_INPUT
+Language=English
+Input WkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_SOURCE
+Language=English
+SourceWkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_PROMPT_WKSTANAME_TARGET
+Language=English
+TargetWkstaName=%0
+.
+MessageId=+1 SymbolicName=IDS_BADARGCOUNT
+Language=English
+Bad number of arguments.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BADMENUSEL
+Language=English
+Your input "%1!ws!" is invalid. One of "%2!ws!" was expected.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INPUTPROFILEPROPERTIES
+Language=English
+Input new profile properties.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INPUTWKSTAPROPERTIES
+Language=English
+Input new workstation properties.%n%0
+.
+MessageId=+1 SymbolicName=IDS_INVALIDSTRING_CHARSTRINGA
+Language=English
+Invalid string "%1!s!"%n%0
+.
+MessageId=+1 SymbolicName=IDS_MUSTCLOSEHANDLE
+Language=English
+Must close existing RemoteBoot service handle
+before opening new RemoteBoot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_NOSERVICEHANDLE
+Language=English
+You do not have an open RemoteBoot service handle.%n%0
+.
+MessageId=+1 SymbolicName=IDS_NOTSUPPLIED
+Language=English
+"%1!ws!" must be supplied%n%0
+.
+MessageId=+1 SymbolicName=IDS_INVALID_INPUT
+Language=English
+No valid input supplied%n%0
+.
+MessageId=+1 SymbolicName=IDS_TARGETSERVER
+Language=English
+ServerName = %1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_USAGE
+Language=English
+The RPLCMD utility can be used to view and update the Remoteboot service
+database. The Remoteboot service must already be running.%n
+%n
+RPLCMD [\\computername]%n
+%n
+\\computername Specifies a remote computer where the Remoteboot service%n
+ is running. If this parameter is omitted, RPLCMD manages%n
+ the Remoteboot service on the local computer.%n
+.
+MessageId=+1 SymbolicName=IDS_PARAMETERSOPTIONAL
+Language=English
+%tAll other parameters are optional%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERBADFLAGS
+Language=English
+%tBad Adapter flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERCOMMENT
+Language=English
+%tAdapterComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BBCFILE
+Language=English
+%tBbcFile=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTACK_FALSE
+Language=English
+%tAcknowledgment of the last RemoteBoot frame is not requested.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTACK_TRUE
+Language=English
+%tAcknowledgment of the last RemoteBoot frame is requested.%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTBADFLAGS
+Language=English
+%tBad Boot flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTCOMMENT
+Language=English
+%tBootComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTNAME
+Language=English
+%tBootName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGBADFLAGS
+Language=English
+%tBad Configuration flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGCOMMENT
+Language=English
+%tConfigComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGDISABLED
+Language=English
+%tConfiguration is disabled.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGENABLED
+Language=English
+%tConfiguration is enabled.%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGNAME
+Language=English
+%tConfigName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME
+Language=English
+%tDirName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME2
+Language=English
+%tDirName2=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME3
+Language=English
+%tDirName3=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_DIRNAME4
+Language=English
+%tDirName4=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITFILE
+Language=English
+%tFitFile=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITPERSONAL
+Language=English
+%tFitPersonal=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_FITSHARED
+Language=English
+%tFitShared=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILEBADFLAGS
+Language=English
+%tBad Profile flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILECOMMENT
+Language=English
+%tProfileComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILENODISKTREE
+Language=English
+%tDisk tree for this profile may be absent.%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPADDRESS
+Language=English
+%tTcpIpAddress=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPGATEWAY
+Language=English
+%tTcpIpGateway=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_TCPIPSUBNET
+Language=English
+%tTcpIpSubnet=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_SERVICEBADFLAGS
+Language=English
+%tBad Service flags, Flags=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORBADFLAGS
+Language=English
+%tBad Vendor flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORCOMMENT
+Language=English
+%tVendorComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORNAME
+Language=English
+%tVendorName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WINDOWSIZE
+Language=English
+%tWindowSize=0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTAADAPTER
+Language=English
+%tAdapterName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTACOMMENT
+Language=English
+%tWkstaComment=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTAINPROFILE
+Language=English
+%tProfileName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONBADFLAGS
+Language=English
+%tBad Remoteboot password flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONNONE
+Language=English
+%tRemoteBoot password is not solicited%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONOPT
+Language=English
+%tRemoteBoot password is optional%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTALOGONREQD
+Language=English
+%tRemoteBoot password is requred%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGBADFLAGS
+Language=English
+%tBad Remoteboot home directory flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGFALSE
+Language=English
+%tUses private home directory%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTASHARINGTRUE
+Language=English
+%tUses shared home directory%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_TRUE
+Language=English
+%tUser will be deleted when workstation is deleted by Remoteboot Manager%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_FALSE
+Language=English
+%tUser will be not deleted when workstation is deleted by Remoteboot Manager%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DELETE_BAD_FLAGS
+Language=English
+%tBad DELETE flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_TRUE
+Language=English
+%tUses DHCP (when TCP/IP is enabled)%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_FALSE
+Language=English
+%tDoes not use DHCP (when TCP/IP is enabled)%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTA_DHCP_BAD_FLAGS
+Language=English
+%tBad DHCP flags, Flags = 0x%1!x!%n%0
+.
+MessageId=+1 SymbolicName=IDS_PROFILENAME_NOTAB
+Language=English
+ProfileName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_ADAPTERNAME_NOTAB
+Language=English
+AdapterName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_BOOTNAME_NOTAB
+Language=English
+BootName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_CONFIGNAME_NOTAB
+Language=English
+ConfigName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_VENDORNAME_NOTAB
+Language=English
+VendorName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_WKSTANAME_NOTAB
+Language=English
+WkstaName=%1!ws!%n%0
+.
+MessageId=+1 SymbolicName=IDS_RPLSVC_COMPUTER
+Language=English
+Managing remoteboot service at computer %1!ws!.%n%0
+.
+MessageId=+1 SymbolicName=IDS_RPLSVC_LOCAL_COMPUTER
+Language=English
+Managing remoteboot service at local computer.%n%0
+.
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/command/rplver.rc b/private/net/svcdlls/rpl/command/rplver.rc
new file mode 100644
index 000000000..52c98ef82
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/rplver.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Command-line Utility"
+#define VER_INTERNALNAME_STR "RPLCMD.EXE"
+#define VER_ORIGINALFILENAME_STR "RPLCMD.EXE"
+
+#include "common.ver"
+#include "rplmsg.rc"
diff --git a/private/net/svcdlls/rpl/command/sources b/private/net/svcdlls/rpl/command/sources
new file mode 100644
index 000000000..c546b3668
--- /dev/null
+++ b/private/net/svcdlls/rpl/command/sources
@@ -0,0 +1,65 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 08 - April - 1994
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP = net
+MINORCOMP = rpl
+TARGETNAME= rplcmd
+
+
+
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\..\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplcmd.c \
+ rplver.rc
+
+
+UMTYPE= console
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H
+
+
+NTTARGETFILE0= \
+ rplmsg.h \
+ rplmsg.mc
+
+
diff --git a/private/net/svcdlls/rpl/convert/adapter.c b/private/net/svcdlls/rpl/convert/adapter.c
new file mode 100644
index 000000000..738dbb67c
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/adapter.c
@@ -0,0 +1,71 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Creates adapter table to be used with NT rpl service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLADAPTER_ALLOCATE
+#include "adapter.h"
+#undef RPLADAPTER_ALLOCATE
+
+
+DWORD AdapterCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, ADAPTER_TABLE_NAME,
+ ADAPTER_TABLE_PAGE_COUNT, ADAPTER_TABLE_DENSITY, &AdapterTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < ADAPTER_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = AdapterTable[ index].ColumnType;
+
+ CallM( JetAddColumn( SesId, AdapterTableId,
+ AdapterTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &AdapterTable[ index].ColumnId));
+ }
+
+ Offset = AddKey( IndexKey, '+', AdapterTable[ ADAPTER_AdapterName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, AdapterTableId, ADAPTER_INDEX_AdapterName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/boot.c b/private/net/svcdlls/rpl/convert/boot.c
new file mode 100644
index 000000000..e0dacb4ec
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/boot.c
@@ -0,0 +1,306 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ boot.c
+
+Abstract:
+
+ Creates boot block table (server record table). Parses old style
+ RPL.MAP server records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLBOOT_ALLOCATE
+#include "boot.h"
+#undef RPLBOOT_ALLOCATE
+
+
+DWORD BootCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, BOOT_TABLE_NAME,
+ BOOT_TABLE_PAGE_COUNT, BOOT_TABLE_DENSITY, &BootTableId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateTable failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < BOOT_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = BootTable[ index].ColumnType;
+
+ JetError = JetAddColumn( SesId, BootTableId,
+ BootTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &BootTable[ index].ColumnId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("AddColumn( %s) failed err=%d", BootTable[ index].ColumnName, JetError));
+ return( MapJetError( JetError));
+ }
+ }
+
+ //
+ // Boot names of DOS boot blocks may be shared by several adapter id
+ // classes (vendor codes). Therefore, server name (== boot name)
+ // cannot be a unique index nor a primary index. But you can construct
+ // a primary index from VendorId & BootName.
+ // +VendorId+BootName index is used to enumerate all boot names for a
+ // given vendor id - which is then used to enumerate all profiles & all
+ // configs compatible with a given vendor id.
+ //
+ Offset = AddKey( IndexKey, '+', BootTable[ BOOT_VendorId].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', BootTable[ BOOT_BootName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, BootTableId, BOOT_INDEX_VendorIdBootName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ Offset = AddKey( IndexKey, '+', BootTable[ BOOT_BootName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, BootTableId, BOOT_INDEX_BootName,
+ 0, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make boot name a current index - used to validate configs.
+ //
+ JetError = JetSetCurrentIndex( SesId, BootTableId, BOOT_INDEX_BootName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ProcessBoot( PWCHAR * Fields)
+{
+//
+// Boot block record (server record) defines.
+//
+#define BOOT_BBCFILE_INDEX 1 // boot block configuration file
+#define BOOT_RETRYCOUNT_INDEX 2 // retry count (used with default boot)
+#define BOOT_TIMEOUT_INDEX 3 // retry period (used with default boot)
+#define BOOT_ACKSTATUS_INDEX 4 // used with acknowledgments
+#define BOOT_COMMENT_INDEX 6 // boot block comment
+#define BOOT_VENDOR_INDEX 7 // common adapter id digits
+#define BOOT_BOOTNAME_INDEX 13 // boot block identifier
+
+#define BOOT_DEFAULT_RETRYCOUNT 3
+#define BOOT_DEFAULT_TIMEOUT 10
+#define USE_ACK_CHAR L'A'
+#define FINAL_ACK_ONLY_CHAR L'F'
+#define PARTIAL_ACK_CHAR L'P'
+#define NO_ACK_CHAR L'N'
+#define ADAPT_WIN_CHAR L'W'
+
+ PWCHAR VendorName; // common adapter id digits
+ DWORD VendorId; // common adapter id digits
+ PWCHAR BootName; // id of boot block record
+ PWCHAR BootComment;
+ PWCHAR BbcFile; // boot block configuration file relative path
+#ifdef RPL_OBSOLETE
+ DWORD RetryCount;
+ DWORD RetryPeriod;
+#endif
+ DWORD WindowSize; // used with acknowledgments
+ //
+ // SendFileRequest is of type JET_coltypBit, which is of UCHAR size.
+ // Thus it is declared as BOOLEAN == UCHAR, instead of BOOL == int.
+ //
+ BOOLEAN FinalAck; // used with acknowledgments
+ DWORD Flags;
+
+#ifdef RPL_OBSOLETE
+ //
+ // Initialize retry count & retry period (else use defaults).
+ //
+ if ( iswdigit( *Fields[ BOOT_TIMEOUT_INDEX]) &&
+ iswdigit( *Fields[ BOOT_RETRYCOUNT_INDEX])) {
+ RetryPeriod = wcstoul( Fields[ BOOT_TIMEOUT_INDEX], NULL, 0);
+ RetryCount = wcstoul( Fields[ BOOT_RETRYCOUNT_INDEX], NULL, 0);
+ } else {
+ RetryPeriod = BOOT_DEFAULT_TIMEOUT;
+ RetryCount = BOOT_DEFAULT_RETRYCOUNT;
+ }
+#endif
+
+ switch( toupper( *Fields[ BOOT_ACKSTATUS_INDEX])) {
+ case FINAL_ACK_ONLY_CHAR:
+ WindowSize = MAXWORD; // don't ack any (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ case PARTIAL_ACK_CHAR:
+ WindowSize = 0; // ack every (non-final) packet
+ FinalAck = FALSE; // don't ack the final packet
+ break;
+ case ADAPT_WIN_CHAR:
+ if ( Fields[ BOOT_ACKSTATUS_INDEX][ 1] != EQUALS_CHAR) {
+ WindowSize = 0; // the default is to ack every (non-final) packet
+ } else {
+ WindowSize = wcstoul( Fields[BOOT_ACKSTATUS_INDEX] + 2, NULL, 0);
+ //
+ // Algorithm adds an extra packet to window, decrement counter
+ // by one. (Thus window size of 1 is same as USE_ACK_CHAR.)
+ //
+ if ( WindowSize) { // avoid underflow
+ WindowSize--;
+ }
+ } // ack every WindowSize-th (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ case NO_ACK_CHAR:
+ case 0:
+ WindowSize = MAXWORD; // don't ack any (non-final) packet
+ FinalAck = FALSE; // don't ack the final packet
+ break;
+ default:
+ DbgUserBreakPoint();
+ NOTHING; // fall through to most common case
+ case USE_ACK_CHAR:
+ WindowSize = 0; // ack every (non-final) packet
+ FinalAck = TRUE; // ack the final packet
+ break;
+ }
+ WindowSize = 0; // BUGBUG temporary workaround for RPLSVC bug
+
+ if ( FinalAck == TRUE) {
+ Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE;
+ } else {
+ Flags = BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE;
+ }
+
+ BootComment = Fields[ BOOT_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( BootComment)) {
+ BootComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+
+ //
+ // Note that server identifier has leading 'R' (enabled) or 'D'
+ // (disabled) in wksta record but it always has leading 'R' in server
+ // record. We do not want to save leading 'R' in server record.
+ //
+ BootName = Fields[ BOOT_BOOTNAME_INDEX] + 1; // add 1 to skip leading 'R'
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_BootNameTooLong, BootName);
+ return;
+ }
+
+ BbcFile = Fields[ BOOT_BBCFILE_INDEX];
+ if ( !ValidName( BbcFile, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_BbcFileTooLong, BootName, BbcFile);
+ return;
+ }
+
+ //
+ // LM2.2 introduced multiple vendor codes in the vendor field of a
+ // boot block record. For each of the vendor codes we create a
+ // separate jet record.
+ //
+ // Store common adapter id digits both as a hex string (VendorName)
+ // and as a dword (VendorId).
+ //
+ for ( VendorName = wcstok( Fields[ BOOT_VENDOR_INDEX], L"|");
+ VendorName != NULL; VendorName = wcstok( NULL, L"|")) {
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_BadVendorName, BootName, VendorName);
+ continue;
+ }
+
+ VendorId = wcstoul( VendorName, NULL, 16); // since VendorName must be hex
+
+ Call( JetPrepareUpdate( SesId, BootTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BootName].ColumnId,
+ BootName, (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BootComment].ColumnId,
+ BootComment, (wcslen( BootComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_Flags].ColumnId,
+ &Flags, sizeof( Flags), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_VendorName].ColumnId,
+ VendorName, (wcslen( VendorName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId,
+ BbcFile, (wcslen( BbcFile) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId,
+ &WindowSize, sizeof( WindowSize), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId,
+ &VendorId, sizeof( VendorId), 0, NULL));
+
+#ifdef RPL_OBSOLETE
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_RetryCount].ColumnId,
+ &RetryCount, sizeof( RetryCount), 0, NULL));
+
+ Call( JetSetColumn( SesId, BootTableId,
+ BootTable[ BOOT_RetryPeriod].ColumnId,
+ &RetryPeriod, sizeof( RetryPeriod), 0, NULL));
+#endif
+
+ Call( JetUpdate( SesId, BootTableId, NULL, 0, NULL));
+ }
+}
+
+
+BOOL FindBoot( IN PWCHAR BootName)
+{
+ return( Find( BootTableId, BootName));
+}
+
+
+VOID BootListTable( VOID)
+{
+ ListTable( BOOT_TABLE_NAME, BootTable[ BOOT_BootName].ColumnName,
+ BOOT_INDEX_BootName);
+}
+
+
diff --git a/private/net/svcdlls/rpl/convert/config.c b/private/net/svcdlls/rpl/convert/config.c
new file mode 100644
index 000000000..99215c4f5
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/config.c
@@ -0,0 +1,527 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ Creates config table. Parses old style RPLMGR.INI config records
+ and creates corresponding entries in jet database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLCONFIG_ALLOCATE
+#include "config.h"
+#undef RPLCONFIG_ALLOCATE
+
+PWCHAR RplmgrIniFile = L"rplmgr.ini";
+
+#define MAX_RPLMGR_INI_FILE_SIZE 0xFFFF // BUGBUG some arbitrary value
+
+#define NO_SCRIPT_FIELDS 13
+
+#define SECTION_STARTMARK L'['
+#define CONFIG_BEGIN L"[configuration]"
+
+#define CARRIAGE_RETURN_CHAR L'\r' // 0xD or \015
+
+// #define RPL_DEBUG_ALL
+
+
+BOOL GetConfigFieldValue(
+ IN PWCHAR Cursor,
+ IN DWORD Index
+ )
+/*++
+ Fields with multiple field values, such as AdapterName, are
+ not properly parsed here. Howerever, we do not care about
+ the values of these fields.
+--*/
+{
+ PWCHAR End;
+ DWORD Length;
+
+ //
+ // By default the field is empty.
+ //
+ ConfigParseTable[ Index].Value = NULL;
+ ConfigParseTable[ Index].Size = 0;
+
+ //
+ // Read to EQUALS_CHAR
+ //
+ while ( *Cursor != EQUALS_CHAR && *Cursor != COMMENT_CHAR &&
+ *Cursor != CARRIAGE_RETURN_CHAR) {
+ Cursor++;
+ }
+
+ if ( *Cursor == COMMENT_CHAR || *Cursor == CARRIAGE_RETURN_CHAR) {
+ return( TRUE);
+ }
+
+ Cursor++; // Skip EQUALS_CHAR
+
+ //
+ // Read to the beginning of field value.
+ //
+ while( *Cursor != CARRIAGE_RETURN_CHAR && iswspace(*Cursor) &&
+ *Cursor != COMMENT_CHAR) {
+ Cursor++;
+ }
+
+ if ( *Cursor == COMMENT_CHAR || *Cursor == CARRIAGE_RETURN_CHAR) {
+ return( TRUE);
+ }
+
+ //
+ // Make sure that boot block reference is enabled.
+ //
+ if ( Index == CONFIG_BootName) {
+ if ( *Cursor != L'R') {
+ RplAssert( TRUE, ("Bad boot block id: %.40ws", Cursor));
+ return( FALSE);
+ }
+ Cursor++; // skip leading 'R' in boot block name
+ }
+
+ //
+ // Find the end point of this field. Since comments may contain spaces,
+ // space is not used as a separator for CONFIG_ConfigComment field.
+ //
+
+ End = wcspbrk( Cursor, Index == CONFIG_ConfigComment ? L"\f\n\r\t\v" : L" \f\n\r\t\v");
+ if ( End != NULL) {
+ Length = End - Cursor;
+ } else {
+ Length = wcslen( Cursor);
+ }
+
+ ConfigParseTable[ Index].Value = GlobalAlloc( GMEM_FIXED, (Length+1)*sizeof(WCHAR));
+ if ( ConfigParseTable[ Index].Value == NULL) {
+ DWORD Error = GetLastError();
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", Error));
+ return( FALSE);
+ }
+ ConfigParseTable[ Index].Size = (Length+1)*sizeof(WCHAR);
+ memcpy( ConfigParseTable[ Index].Value, Cursor, Length * sizeof(WCHAR));
+ ((PWCHAR)ConfigParseTable[ Index].Value)[ Length] = 0;
+ return( TRUE);
+}
+
+
+BOOL StringISpaceEqual( IN PWCHAR str1, IN PWCHAR str2 )
+/*++
+
+Routine Description:
+
+ Case insensitive version of StringsSpaceEqual.
+
+Arguments:
+
+ str1 - first string
+ str2 - second string
+
+Return Value:
+
+ TRUE if strings are equal, FALSE otherwise.
+
+--*/
+{
+ WCHAR ch1;
+
+#ifdef NOT_YET
+ if ( str1 == NULL || str2 == NULL) {
+ str1 = (str1 != NULL) ? str1 : str2;
+ if ( str2 == NULL) {
+ return( TRUE); // both strings are NULL
+ }
+ while ( iswspace( *str1++)) {
+ NOTHING; // check if not NULL string contains spaces only
+ }
+ return( *str1 == 0);
+ }
+#endif // NOT_YET
+
+ //
+ // Compare strings until the first space or NULL character
+ // (in the first string) or until we find the first different character.
+ //
+ while ( (ch1 = toupper(*str1)) && !iswspace(ch1) && ch1 == toupper(*str2)) {
+ str1++, str2++;
+ }
+
+ //
+ // For strings to be equal, both characters must be NULL or space.
+ //
+ if ( (!ch1 || iswspace(ch1)) && (!(ch1 = *str2) || iswspace(ch1)))
+ {
+ return( TRUE);
+ }
+
+ return( FALSE);
+}
+
+
+#ifdef RPL_DEBUG_ALL
+VOID ProcessConfigDisplay( VOID)
+{
+ DWORD Index;
+
+ printf( "\tCONFIGURATION\n");
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue;
+ }
+ printf( "%ws %ws 0x%x\n", ConfigParseTable[ Index].Name,
+ ConfigParseTable[ Index].Value, ConfigParseTable[ Index].Size);
+ }
+}
+#endif
+
+
+PWCHAR ProcessConfig( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ We read all the fields in the Config[] array, then use jet apis
+ to store this config in the database.
+
+Arguments:
+
+ Cursor - at entry this points to the beginning of the config section
+
+Return Value:
+
+ Cursor value after we finished processing the current config section.
+
+--*/
+{
+#define INVALID_ERROR_PARAMETER ((DWORD)(-1))
+ DWORD Index;
+ JET_ERR JetError;
+ DWORD Flags;
+ DWORD Offset;
+ DWORD ErrorParameter;
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Value != NULL) {
+ GlobalFree( ConfigParseTable[ Index].Value);
+ }
+ ConfigParseTable[ Index].Value = NULL;
+ }
+ if ( OffsetAfterComment( Cursor) == 0) {
+ Flags = CONFIG_FLAGS_ENABLED_TRUE;
+ } else {
+ Flags = CONFIG_FLAGS_ENABLED_FALSE;
+ }
+
+ for ( Cursor=GetNextLine(Cursor), ErrorParameter = INVALID_ERROR_PARAMETER;
+ *Cursor!=0; Cursor=GetNextLine(Cursor)) {
+
+ Offset = OffsetAfterComment( Cursor);
+ if ( *(Cursor+Offset) == SECTION_STARTMARK) {
+ break; // start of a new section, write the current section
+ }
+ Cursor += Offset;
+
+ //
+ // Find value for this name.
+ //
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue; // skip this one, not a RPLMGR.INI keyword
+ }
+ if ( !StringISpaceEqual( Cursor, ConfigParseTable[ Index].Name)) {
+ continue; // no match with this keyword
+ }
+ if ( !GetConfigFieldValue( Cursor, Index)) {
+ if ( ErrorParameter == INVALID_ERROR_PARAMETER) {
+ ErrorParameter = Index;
+ }
+ } else if ( Index == CONFIG_FitShared || Index == CONFIG_FitPersonal) {
+ PWCHAR Value;
+ Value = AddFileExtension( ConfigParseTable[ Index].Value,
+ L".FIT", TRUE);
+ if ( Value == NULL) {
+ if ( ErrorParameter == INVALID_ERROR_PARAMETER) {
+ ErrorParameter = Index;
+ }
+ } else if ( Value != ConfigParseTable[ Index].Value) {
+ GlobalFree( ConfigParseTable[ Index].Value);
+ ConfigParseTable[ Index].Value = Value;
+ ConfigParseTable[ Index].Size = (wcslen(Value)+1)*sizeof(WCHAR);
+ }
+ }
+ break;
+ }
+ }
+#ifdef RPL_DEBUG_ALL
+ ProcessConfigDisplay();
+#endif
+
+ if ( ConfigParseTable[ CONFIG_ConfigName].Value == NULL) {
+ RplAssert( TRUE, ("Bad config"));
+ return( Cursor);
+ }
+
+ //
+ // Perform same checks as in NetrRplConfigAdd.
+ //
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ NOTHING; // do not overwrite the first error
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_FitPersonal].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitPersonal;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_FitShared].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitShared;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName4].Value, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName4;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName3].Value, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName3;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName2].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName2;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_DirName].Value, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_BootName].Value, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_BootName;
+ } else if ( !ValidName( ConfigParseTable[ CONFIG_ConfigName].Value, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_ConfigName;
+ }
+
+ if ( ErrorParameter < CONFIG_TABLE_LENGTH) {
+ RplPrintf2( RPLI_CVT_ConfigInvalid, ConfigParseTable[ CONFIG_ConfigName].Value, ConfigParseTable[ ErrorParameter].Name);
+ return( Cursor);
+ }
+
+ if ( ConfigParseTable[ CONFIG_FitPersonal].Value == NULL) {
+ //
+ // Ignore default boot configurations.
+ //
+ RplPrintf1( RPLI_CVT_ConfigNoPersonalFIT, ConfigParseTable[ CONFIG_ConfigName].Value);
+ return( Cursor);
+ }
+
+ if ( !_wcsicmp( L"OS2", ConfigParseTable[ CONFIG_DirName].Value )) {
+ //
+ // Ignore OS/2 configurations.
+ //
+ RplPrintf1( RPLI_CVT_ConfigNoOS2, ConfigParseTable[ CONFIG_ConfigName].Value);
+ return( Cursor);
+ }
+
+ if ( RPL_STRING_TOO_LONG( ConfigParseTable[ CONFIG_ConfigComment].Value)) {
+ //
+ // Silently truncate comments that are too long.
+ //
+ ((PWCHAR)ConfigParseTable[ CONFIG_ConfigComment].Value)[ RPL_MAX_STRING_LENGTH] = 0;
+ ConfigParseTable[ CONFIG_ConfigComment].Size = (RPL_MAX_STRING_LENGTH+1)*sizeof(WCHAR);
+ }
+
+ _wcsupr( (PWCHAR)ConfigParseTable[ CONFIG_BootName].Value);
+ _wcsupr( (PWCHAR)ConfigParseTable[ CONFIG_ConfigName].Value);
+
+ Call( JetPrepareUpdate( SesId, ConfigTableId, JET_prepInsert));
+
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++ ) {
+ if ( ConfigParseTable[ Index].Name == NULL) {
+ continue;
+ }
+ JetError = JetSetColumn( SesId, ConfigTableId,
+ ConfigTable[ Index].ColumnId, ConfigParseTable[ Index].Value,
+ ConfigParseTable[ Index].Size, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("Index=%d(dec) JetError=%d", Index, JetError));
+ }
+ }
+ Call( JetSetColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId, &Flags, sizeof(Flags), 0, NULL));
+
+ Call( JetUpdate( SesId, ConfigTableId, NULL, 0, NULL));
+ return( Cursor);
+}
+
+
+
+DWORD ProcessRplmgrIni( IN LPWSTR FilePath)
+{
+ PWCHAR Cursor;
+ PWCHAR String;
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD Index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, "config", CONFIG_TABLE_PAGE_COUNT,
+ CONFIG_TABLE_DENSITY, &ConfigTableId);
+
+ ColumnDef.cbStruct = sizeof( ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.coltyp = JET_coltypLong;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0;
+
+ //
+ // Create columns. First initialize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( Index = 0; Index < CONFIG_TABLE_LENGTH; Index++ ) {
+
+ ColumnDef.coltyp = ConfigTable[ Index].ColumnType;
+ JetError = JetAddColumn( SesId, ConfigTableId,
+ ConfigTable[ Index].ColumnName, &ColumnDef,
+ NULL, 0, &ConfigTable[ Index].ColumnId);
+ if( JetError != ERROR_SUCCESS ) {
+ return( MapJetError( JetError));
+ }
+ }
+
+ Offset = AddKey( IndexKey, '+', ConfigTable[ CONFIG_ConfigName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ConfigTableId, CONFIG_INDEX_ConfigName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+ConfigName index is used to enumerate all configurations for
+ // a given VendorId.
+ //
+ Offset = AddKey( IndexKey, '+', ConfigTable[ CONFIG_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ConfigTable[ CONFIG_ConfigName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ConfigTableId, CONFIG_INDEX_BootNameConfigName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make config name a current index - used to validate profiles.
+ //
+ JetError = JetSetCurrentIndex( SesId, ConfigTableId, CONFIG_INDEX_ConfigName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ String = ReadTextFile( FilePath, RplmgrIniFile, MAX_RPLMGR_INI_FILE_SIZE);
+ if ( String == NULL) {
+ return( GetLastError());
+ }
+
+ //
+ // If current line is the beginning of configuration, we process
+ // that configuration (note that this processing has the side effect of
+ // advancing the cursor), else we just advance the cursor.
+ //
+ for ( Cursor = GetFirstLine( String); *Cursor != 0; NOTHING) {
+ Offset = OffsetAfterComment( Cursor);
+ if ( StringISpaceEqual( Cursor+Offset, CONFIG_BEGIN)) {
+ Cursor = ProcessConfig( Cursor);
+ } else {
+ Cursor = GetNextLine( Cursor);
+
+ }
+ }
+
+ if ( GlobalFree( String) != NULL) {
+ RplAssert( TRUE, ("GlobalFree: Error=%d", GetLastError()));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ConfigPruneTable( VOID)
+/*++
+ Eliminate config records that do not have a corresponding
+ boot block record defined.
+--*/
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, ConfigTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, ConfigTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ConfigTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+#ifdef RPL_DEBUG_ALL
+ WCHAR Value[ 20];
+ JetRetrieveColumn( SesId, ConfigTableId,
+ ConfigTable[ CONFIG_ConfigName].ColumnId, Value,
+ sizeof( Value), &NameSize, 0, NULL);
+ printf("Deleting config %ws since boot %ws was not found\n",
+ Value, Name);
+#endif
+ Call( JetDelete( SesId, ConfigTableId));
+ continue;
+ }
+ }
+}
+
+
+BOOL FindConfig( IN PWCHAR Name)
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( SesId, ConfigTableId, JET_MoveFirst, 0);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetMove failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, ConfigTableId, Name,
+ ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, ConfigTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetSeek for %ws failed err=%d", Name, JetError));
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID ConfigListTable( VOID)
+{
+ ListTable( CONFIG_TABLE_NAME, ConfigTable[ CONFIG_ConfigName].ColumnName,
+ CONFIG_INDEX_ConfigName);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/convert.rc b/private/net/svcdlls/rpl/convert/convert.rc
new file mode 100644
index 000000000..bec8ec87c
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/convert.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Conversion Utility"
+#define VER_INTERNALNAME_STR "rplcnv.exe"
+#define VER_ORIGINALFILENAME_STR "rplcnv.exe"
+
+#include "common.ver"
+
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/rpl/convert/debug.c b/private/net/svcdlls/rpl/convert/debug.c
new file mode 100644
index 000000000..918c93752
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/debug.c
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module.
+
+Author:
+
+ Jon Newman 26 - April - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+ Built from server\debug.c
+
+--*/
+
+#include "local.h"
+#include "rpldebug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+char RG_DebugPublicBuffer[ 120];
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ strcpy( RG_DebugPublicBuffer, RPL_PROMPT );
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer); // for kernel debugger
+ printf( "%s\n", RG_DebugPublicBuffer); // for user debugger
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ DbgPrint( "[RplSvc] Running checked version of service on a free build.\n");
+ printf( "[RplSvc] Running checked version of service on a free build.\n");
+ DbgUserBreakPoint();
+ }
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/convert/library.c b/private/net/svcdlls/rpl/convert/library.c
new file mode 100644
index 000000000..1014cbef2
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/library.c
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ library.c
+
+Abstract:
+
+ Common routines used in CONVERT code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+ Jon Newman (jonn) 06 - June - 1994
+ Convert file from OEM code page
+
+--*/
+
+#include "local.h"
+
+
+LPWSTR ReadTextFile(
+ IN LPWSTR FilePath,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ )
+/*++
+
+Routine Description:
+
+ Reads text file, converts its content from dbcs to unicode, and returns
+ a pointer to newly allocate unicode buffer.
+
+Arguments:
+
+Return Value:
+
+ Pointer to unicode buffer table if successful, NULL otherwise.
+
+--*/
+{
+ PBYTE DbcsString = NULL;
+ DWORD DbcsSize;
+ PWCHAR UnicodeString = NULL;
+ DWORD UnicodeSize;
+ int UnicodeStringLength;
+ HANDLE FileHandle;
+ DWORD BytesRead;
+ BOOL success = FALSE;
+ PWCHAR pWchar;
+ WCHAR CompleteFilePath[ MAX_PATH+1];
+ PWCHAR UseFilePath = FileName;
+
+ CompleteFilePath[0] = L'\0';
+ if ( FilePath != NULL && lstrlenW(FilePath) > 0) {
+ lstrcpyW( CompleteFilePath, FilePath);
+ if ( CompleteFilePath[ lstrlenW(CompleteFilePath)-1 ] != L'\\') {
+ lstrcatW( CompleteFilePath, L"\\");
+ }
+ if ( FileName != NULL) {
+ lstrcatW( CompleteFilePath, FileName );
+ }
+ UseFilePath = CompleteFilePath;
+ }
+
+ FileHandle = CreateFile( UseFilePath, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ //
+ // Specifying a bad path (path with missing OS/2 rpl init files)
+ // is a common user error. Print out something for the user.
+ //
+ RplAssert( TRUE, ("CreateFile: Error = %d", GetLastError()));
+ RplPrintf1( RPLI_CVT_CannotOpenFile, UseFilePath);
+ goto exit;
+ }
+ DbcsSize = GetFileSize( FileHandle, NULL); // does not include 0x1A at the file end
+ if ( DbcsSize == INVALID_FILE_SIZE || DbcsSize > MaxFileSize) {
+ RplAssert( TRUE, ("DbcsSize = %d", DbcsSize));
+ goto exit;
+ }
+ DbcsString = GlobalAlloc( GMEM_FIXED, DbcsSize);
+ if ( DbcsString == NULL) {
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ UnicodeSize = ( DbcsSize + 1) * sizeof(WCHAR); // extra 1 for terminating NULL
+ UnicodeString = GlobalAlloc( GMEM_FIXED, UnicodeSize);
+ if ( UnicodeString == NULL) {
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ if ( !ReadFile( FileHandle, DbcsString, DbcsSize, &BytesRead, NULL)) {
+ RplAssert( TRUE, ("ReadFile: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ if ( BytesRead != DbcsSize) {
+ RplAssert( TRUE, ("BytesRead = %d, DbcsSize", BytesRead, DbcsSize));
+ goto exit;
+ }
+
+ UnicodeStringLength = MultiByteToWideChar(
+ CP_OEMCP, // file is in OEM codepage
+ MB_PRECOMPOSED, DbcsString, DbcsSize, UnicodeString, UnicodeSize);
+ if ( UnicodeStringLength == 0) {
+ RplAssert( TRUE, ("MultiByte...: Error = %d", GetLastError()));
+ goto exit;
+ }
+
+ //
+ // If file has END_OF_TEXT_FILE_CHAR, truncate the text there.
+ //
+ pWchar = wcschr( UnicodeString, END_OF_TEXT_FILE_CHAR);
+ if ( pWchar != NULL) {
+ *pWchar = 0;
+ }
+
+ success = TRUE;
+
+exit:
+
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ if ( DbcsString != NULL) {
+ GlobalFree( DbcsString);
+ }
+
+ if ( success != TRUE && UnicodeString != NULL) {
+ GlobalFree( UnicodeString);
+ UnicodeString = NULL;
+ }
+
+ return( UnicodeString);
+
+} // ReadTextFile
+
+
+PWCHAR GetFirstLine( PWCHAR Cursor)
+/*++
+ Skips all white characters.
+--*/
+//
+{
+ // Read empty chars if there's some.
+ while( *Cursor != 0 && iswspace(*Cursor)) {
+ Cursor++;
+ }
+
+ return( Cursor);
+}
+
+
+
+PWCHAR GetNextLine( PWCHAR Cursor)
+/*++
+ Skips to the end of the current line.
+ Then skips all white characters.
+--*/
+//
+{
+ // Read to end of line.
+ do {
+ Cursor++;
+ } while ( *Cursor != 0 && *Cursor != NEW_LINE_CHAR);
+
+ // Read empty chars if there's some.
+ while( *Cursor != 0 && iswspace(*Cursor)) {
+ Cursor++;
+ }
+
+ return( Cursor);
+}
+
+
+DWORD OffsetAfterComment( IN PWCHAR Cursor)
+/*++
+ Returns the offset of the first non-white non-newline character
+ after an optional RPL_COMMENT. This routine does not & should
+ not cross line boundaries (this is reserved for GetNewLine).
+--*/
+{
+#define RPL_COMMENT L";*;"
+#define RPL_COMMENT_LENGTH (sizeof(RPL_COMMENT)/sizeof(WCHAR) - 1)
+
+ PWCHAR End;
+
+ if ( wcsncmp( Cursor, RPL_COMMENT, RPL_COMMENT_LENGTH)) {
+ return( 0); // there is no RPL_COMMENT
+ }
+ for ( End = Cursor + RPL_COMMENT_LENGTH;
+ *End != 0 && *End != NEW_LINE_CHAR && iswspace(*End);
+ End++) {
+ NOTHING;
+ }
+ if ( *End == NEW_LINE_CHAR) {
+ //
+ // We went to far. Must decrement end pointer or
+ // GetNewLine() will advance to the second next line.
+ //
+ End--;
+ }
+ return( (DWORD)(End - Cursor));
+}
+
+
+BOOL Find( IN JET_TABLEID TableId, IN PWCHAR Name)
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( SesId, TableId, JET_MoveFirst, 0);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("JetMove failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, TableId, Name,
+ ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, TableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error => no break for this.
+ //
+ RplDbgPrint(("JetSeek for %ws failed with error = %d.\n", Name, JetError));
+ } else {
+ RplAssert( TRUE, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID Enum( IN JET_TABLEID TableId, IN JET_COLUMNID ColumnId, IN PCHAR IndexName)
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ Call( JetSetCurrentIndex( SesId, TableId, IndexName));
+
+ for ( ForJetError = JetMove( SesId, TableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, TableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, TableId,
+ ColumnId, Name, sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ continue;
+ }
+ RplDbgPrint(( "%ws\n", Name));
+ }
+}
+
+
+VOID ListTable( IN PCHAR TableName, IN PCHAR ColumnName, IN PCHAR IndexName)
+{
+ JET_TABLEID TableId;
+ JET_COLUMNDEF ColumnDef;
+
+ Call( JetOpenTable( SesId, DbId, TableName, NULL, 0,
+ JET_bitTableDenyWrite, &TableId));
+
+ Call( JetGetTableColumnInfo( SesId, TableId, ColumnName, &ColumnDef,
+ sizeof( ColumnDef), JET_ColInfo));
+
+ RplDbgPrint(( "\tTable: %s\n", TableName));
+
+ Enum( TableId, ColumnDef.columnid, IndexName);
+
+ Call( JetCloseTable( SesId, TableId));
+}
+
+
+PWCHAR AddFileExtension(
+ IN PWCHAR FilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ )
+{
+#define DOT_CHAR L'.'
+#define BACK_SLASH_CHAR L'\\'
+ PWCHAR FilePathEx;
+ PWCHAR pDot;
+ DWORD Length;
+ DWORD Error;
+
+ if ( FilePath == NULL) {
+ RplAssert( TRUE, ("FilePath is NULL"));
+ return( NULL);
+ }
+
+ pDot = wcsrchr( FilePath, DOT_CHAR);
+ if ( pDot != NULL) {
+ //
+ // Found a DOT. FilePath may have an extension.
+ //
+ if ( wcschr( pDot, BACK_SLASH_CHAR) == NULL) {
+ //
+ // There is no backslash after the DOT. FilePath has an extension.
+ // Return NULL if caller insists that file should have no extension.
+ //
+ return( ExtensionOK ? FilePath : NULL);
+ }
+ }
+ Length = wcslen( FilePath) + wcslen( FileExtension);
+ FilePathEx = GlobalAlloc( GMEM_FIXED, (Length + 1) * sizeof(WCHAR));
+ if ( FilePathEx == NULL) {
+ Error = GetLastError();
+ RplAssert( TRUE, ("GlobalAlloc: Error = %d", Error));
+ return( NULL);
+ }
+ wcscpy( FilePathEx, FilePath);
+ wcscat( FilePathEx, FileExtension);
+ return( FilePathEx);
+}
diff --git a/private/net/svcdlls/rpl/convert/local.h b/private/net/svcdlls/rpl/convert/local.h
new file mode 100644
index 000000000..658da46bf
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/local.h
@@ -0,0 +1,227 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load CONVERT program.
+ This program converts from RPL.MAP & RPLMGR.INI, used in OS/2
+ lm2.0 and up, to NT database.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+#include <lmrpl.h>
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+#include "nlstxt.h" // RPLI_CVT manifests
+
+#define EQUALS_CHAR L'='
+
+#define RPL_BUGBUG_ERROR 10999 // need to get rid of these
+
+#if DBG
+#define RPL_DEBUG // used in rpldata.h
+#endif
+
+//
+// Declare/Define all global variables.
+//
+
+#include "rpldata.h" // defines/declares global data structures
+
+//
+// jet call macros:
+// Call - jet call ignore error
+// CallR - jet call RETURN on error
+// CallB - jet call return BOOLEAN (false) on error
+// CallM - jet call return MAPPED error
+//
+
+#ifdef RPL_DEBUG
+
+#define RplDbgPrint( _x_) printf( "[DebugPrint] "); printf _x_
+
+#define RplBreakPoint() if ( DebugLevel) DbgUserBreakPoint()
+
+#define RplAssert( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ { \
+ if ( DBG_CONDITION) { \
+ RplDbgPrint( DBG_PRINT_ARGUMENTS); \
+ RplDbgPrint((" File %s, Line %d\n", __FILE__, __LINE__)); \
+ RplBreakPoint(); \
+ } \
+ }
+#define Call( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ } \
+ } \
+ }
+#define CallR( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return; \
+ } \
+ } \
+ }
+
+#define CallB( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return( FALSE); \
+ } \
+ } \
+ }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ return( NERR_RplInternal); \
+ } \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ printf("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ RplBreakPoint(); \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ } \
+ }
+
+#else
+#define RplDbgPrint( _x_)
+#define RplBreakPoint()
+#define RplAssert( DBG_CONDITION, DBG_PRINT_ARGUMENTS)
+#define Call( fn ) { if ( fn < 0) { NOTHING;} }
+#define CallR( fn ) { if ( fn < 0) { return;} }
+#define CallB( fn ) { if ( fn < 0) { return( FALSE);} }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ }
+#endif
+
+
+LPWSTR ReadTextFile( IN LPWSTR FilePath, IN LPWSTR FileName, IN DWORD MaxFileSize);
+PWCHAR GetFirstLine( PWCHAR Cursor);
+PWCHAR GetNextLine( PWCHAR Cursor);
+DWORD OffsetAfterComment( IN PWCHAR Cursor);
+BOOL Find( IN JET_TABLEID TableId, IN PWCHAR Name);
+VOID Enum( IN JET_TABLEID TableId, IN JET_COLUMNID ColumnId, IN PCHAR ndexName);
+VOID ListTable( IN PCHAR TableName, IN PCHAR ColumnName, IN PCHAR IndexName);
+PWCHAR AddFileExtension(
+ IN PWCHAR FilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ );
+
+DWORD BootCreateTable( VOID);
+VOID ProcessBoot( PWCHAR * Fields);
+VOID BootListTable( VOID);
+BOOL FindBoot( IN PWCHAR BootName);
+
+DWORD ProcessRplmgrIni( IN LPWSTR FilePath );
+BOOL FindConfig( IN PWCHAR ConfigName);
+VOID ConfigPruneTable( VOID);
+VOID ConfigListTable( VOID);
+DWORD CloseConfigTable( VOID);
+
+DWORD ProfileCreateTable( VOID);
+BOOL FindProfile( IN PWCHAR ProfileName);
+VOID ProfileListTable( VOID);
+VOID ProcessProfile( PWCHAR * Fields);
+VOID ProfilePruneTable( VOID);
+
+DWORD WkstaCreateTable( VOID);
+VOID ProcessWksta( PWCHAR * Fields);
+VOID WkstaPruneTable( VOID);
+VOID WkstaListTable( VOID);
+BOOL ListWkstaInfo( IN PWCHAR AdapterName);
+
+DWORD AdapterCreateTable( VOID);
+
+DWORD VendorCreateTable( VOID);
+
+
diff --git a/private/net/svcdlls/rpl/convert/makefile b/private/net/svcdlls/rpl/convert/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/convert/makefile.inc b/private/net/svcdlls/rpl/convert/makefile.inc
new file mode 100644
index 000000000..f28149e83
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/makefile.inc
@@ -0,0 +1,4 @@
+nlstxt.rc: msg00001.bin
+
+nlstxt.h msg00001.bin: nlstxt.mc
+ mc -v nlstxt.mc
diff --git a/private/net/svcdlls/rpl/convert/nlstxt.mc b/private/net/svcdlls/rpl/convert/nlstxt.mc
new file mode 100644
index 000000000..8b8e47f8e
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/nlstxt.mc
@@ -0,0 +1,214 @@
+;/*++
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; rplimsg.h
+;
+;Abstract:
+;
+; Definitions for RPLCNV command private errors.
+;
+;Author:
+;
+; Jon Newman (jonn) 21 - April - 1994
+;
+;Revision History:
+;
+; 21-Apr-1994 jonn
+; templated from AT command
+;
+;Notes:
+;
+; This file is generated by the MC tool from the lmatmsg.mc file.
+;
+;--*/
+
+MessageId=11000 SymbolicName=RPLI_CVT_NoMDB
+Language=English
+File system.mdb is absent.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_StopService
+Language=English
+Remote boot service must be stopped for this program to run.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_InitError
+Language=English
+Unexpected initialization error.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_CannotOpenFile
+Language=English
+Cannot open %1!ws!%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalid
+Language=English
+Not converting %1!ws! profile because its name is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidConfig
+Language=English
+Not converting %1!ws! profile because its configuration %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidFit
+Language=English
+Not converting %1!ws! profile because its fit file %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ProfileInvalidBoot
+Language=English
+Not converting %1!ws! profile because its boot block name %2!ws!
+is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_USAGE
+Language=English
+The RPLCNV utility (RemoteBoot conversion program) converts
+rpl.map and rplmgr.ini used by OS/2 LANMAN Remoteboot service into system.mdb
+and rplsvc.mdb files used by NT Remoteboot service. Install the Remoteboot
+service before running the Remoteboot conversion program, and
+make sure a copy of the original system.mdb file is in
+the root of the RPL file tree as specified during RPL installation.%n
+%n
+RPLCNV [Path]%n
+%n
+Path Specifies a fully qualified path to the directory containing the
+rpl.map and rplmgr.ini files used by OS/2 LANMAN RemoteBoot service.
+If this parameter is omitted it is assumed that these files are in the
+current directory or the root of the RPL file tree.
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_NoMAP_INI
+Language=English
+Files rpl.map and/or rplmgr.ini are absent.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_NoSystemMdb
+Language=English
+File system.mdb is absent from the root of the RPL file tree.
+RPLCNV cannot convert rpl.map and rplmgr.ini unless a copy
+of the original system.mdb file is in the root of the RPL file tree.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_RplsvcMdbExists
+Language=English
+File rplsvc.mdb already exists in the root of the RPL file tree.
+RPLCNV will not create a new one until the existing one is moved,
+renamed or deleted.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_OldSystemMdb
+Language=English
+File system.mdb exists in the root of the RPL file tree but is not
+a copy of the original system.mdb file. RPLCNV cannot convert rpl.map
+and rplmgr.ini unless a copy of the original system.mdb file is in the
+root of the RPL file tree.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BootNameTooLong
+Language=English
+Boot block record with name %1!ws! is not converted because
+its name is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BbcFileTooLong
+Language=English
+Boot block record with name %1!ws! is not converted because
+path to boot block configuration file %2!ws! is too long or
+invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_BadVendorName
+Language=English
+Boot block record with name %1!ws! is not converted because
+vendor name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalid
+Language=English
+Workstation record with name %1!ws! is not converted because
+its name is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidAdapter
+Language=English
+Workstation record with name %1!ws! is not converted because
+adapter id %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidLogon
+Language=English
+Workstation record with name %1!ws! is not converted because
+remoteboot username/password prompting field %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidFit
+Language=English
+Workstation record with name %1!ws! is not converted because
+fit name %2!ws! is too long or invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidSharing
+Language=English
+Workstation record with name %1!ws! is not converted because
+sharing type %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDisabledBoot
+Language=English
+Workstation record with name %1!ws! is not converted since its
+reference to block record %2!ws! is disabled.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidBoot
+Language=English
+Workstation record with name %1!ws! is not converted because
+boot block name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaInvalidProfile
+Language=English
+Workstation record with name %1!ws! is not converted because
+profile name %2!ws! is invalid.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDuplicateName
+Language=English
+Workstation record is not converted because it uses a duplicate computer name
+(%1!ws!) or a duplicate adapter id (%2!ws!).%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_WkstaDefaultProfile
+Language=English
+Workstation record with computer name %1!ws! is not converted
+because it uses the obsolete DEFAULT profile.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigNoPersonalFIT
+Language=English
+Not converting %1!ws! configuration because it does not have
+a fit file for personal profiles.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigNoOS2
+Language=English
+Not converting %1!ws! configuration because it is an OS/2
+client configuration.%n
+.
+
+MessageId=+1 SymbolicName=RPLI_CVT_ConfigInvalid
+Language=English
+Not converting %1!ws! configuration because the value of
+keyword %2!ws! is absent or invalid.%n
+.
+
+
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/convert/profile.c b/private/net/svcdlls/rpl/convert/profile.c
new file mode 100644
index 000000000..f8f0c14a8
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/profile.c
@@ -0,0 +1,270 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ profile.c
+
+Abstract:
+
+ Creates profile table. Parses old style RPL.MAP profile records and
+ creates corresponding entries in jet database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLPROFILE_ALLOCATE
+#include "profile.h"
+#undef RPLPROFILE_ALLOCATE
+
+
+DWORD ProfileCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, PROFILE_TABLE_NAME,
+ PROFILE_TABLE_PAGE_COUNT, PROFILE_TABLE_DENSITY, &ProfileTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < PROFILE_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = ProfileTable[ index].ColumnType;
+
+ Call( JetAddColumn( SesId, ProfileTableId,
+ ProfileTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &ProfileTable[ index].ColumnId));
+ }
+
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_ProfileName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+ProfileName index is used to enumerate all profiles for
+ // a given VendorId.
+ //
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_BootNameProfileName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +ConfigName+ProfileName index is used to find if a there is a
+ // profile record for a given configuration record
+ //
+ Offset = AddKey( IndexKey, '+', ProfileTable[ PROFILE_ConfigName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', ProfileTable[ PROFILE_ProfileName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, ProfileTableId, PROFILE_INDEX_ConfigNameProfileName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Make profile name a current index - used to validate wkstas.
+ //
+ JetError = JetSetCurrentIndex( SesId, ProfileTableId, PROFILE_INDEX_ProfileName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+VOID ProcessProfile( PWCHAR * Fields)
+{
+//
+// PROFILE_COMMENT_INDEX would be equal to 15 if we were to count fields
+// from 1 (not 0) and count ",,," as a single field (not 3 fields).
+//
+#define PROFILE_FITFILE_INDEX 3 // common fit file name
+#define PROFILE_CONFIGNAME_INDEX 12 // configuration name
+#define PROFILE_BOOTNAME_INDEX 13 // boot block identifier
+#define PROFILE_PROFILENAME_INDEX 15 // profile name
+#define PROFILE_COMMENT_INDEX 16 // profile comment
+
+ PWCHAR ProfileComment;
+ PWCHAR ProfileName;
+ PWCHAR BootName;
+ PWCHAR ConfigName;
+ PWCHAR FitShared;
+ PWCHAR FitPersonal;
+ DWORD Flags;
+
+ ProfileName = Fields[ PROFILE_PROFILENAME_INDEX];
+ if ( !ValidName( ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_ProfileInvalid, ProfileName);
+ RplAssert( TRUE, ("Bad profile name %ws", ProfileName));
+ return;
+ }
+ _wcsupr( ProfileName);
+
+ FitShared = AddFileExtension( Fields[ PROFILE_FITFILE_INDEX], L".FIT", FALSE);
+ FitPersonal = AddFileExtension( Fields[ PROFILE_FITFILE_INDEX], L"P.FIT", FALSE);
+ if ( !ValidName( FitShared, RPL_MAX_STRING_LENGTH, TRUE) ||
+ !ValidName( FitPersonal, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidFit, ProfileName, Fields[ PROFILE_FITFILE_INDEX]);
+ RplAssert( TRUE, ("Bad fit name %ws", Fields[ PROFILE_FITFILE_INDEX]));
+ return;
+ }
+
+ ConfigName = Fields[ PROFILE_CONFIGNAME_INDEX];
+ if ( !ValidName( ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidConfig, ProfileName, ConfigName);
+ RplAssert( TRUE, ("Bad config name %ws", ConfigName));
+ return;
+ }
+ _wcsupr( ConfigName);
+
+ BootName = Fields[ PROFILE_BOOTNAME_INDEX];
+ RplAssert( *BootName != L'R', ("BootBlockId=%ws", BootName));
+ BootName++; // skip leading 'R' in server name
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidBoot, ProfileName, Fields[ PROFILE_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad boot name %ws", Fields[ PROFILE_BOOTNAME_INDEX]));
+ return;
+ }
+ _wcsupr( BootName);
+
+ ProfileComment = Fields[ PROFILE_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( ProfileComment)) {
+ ProfileComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+ Call( JetPrepareUpdate( SesId, ProfileTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId, ProfileName,
+ (wcslen( ProfileName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileComment].ColumnId, ProfileComment,
+ (wcslen( ProfileComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId, ConfigName,
+ (wcslen( ConfigName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId, BootName,
+ (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_FitShared].ColumnId, FitShared,
+ (wcslen( FitShared) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_FitPersonal].ColumnId, FitPersonal,
+ (wcslen( FitPersonal) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Flags = 0;
+ Call( JetSetColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_Flags].ColumnId, &Flags, sizeof(Flags), 0, NULL));
+
+
+ Call( JetUpdate( SesId, ProfileTableId, NULL, 0, NULL));
+}
+
+
+VOID ProfilePruneTable( VOID)
+/*++
+ Eliminate profile records that do not have a corresponding config record
+ defined.
+--*/
+{
+ WCHAR Name[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, ProfileTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, ProfileTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ if ( !FindConfig( Name)) {
+ WCHAR ProfileName[ 20];
+ DWORD ProfileNameSize;
+ JetError = JetRetrieveColumn( SesId, ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId, ProfileName,
+ sizeof( ProfileName), &ProfileNameSize, 0, NULL);
+ if ( JetError == JET_errSuccess && ProfileNameSize <= sizeof( ProfileName) ) {
+ RplPrintf2( RPLI_CVT_ProfileInvalidConfig, ProfileName, Name);
+ }
+ Call( JetDelete( SesId, ProfileTableId));
+ continue;
+ }
+ }
+}
+
+
+
+BOOL FindProfile( IN PWCHAR ProfileName)
+{
+ return( Find( ProfileTableId, ProfileName));
+}
+
+
+
+VOID ProfileListTable( VOID)
+{
+ ListTable( PROFILE_TABLE_NAME, ProfileTable[ PROFILE_ProfileName].ColumnName,
+ PROFILE_INDEX_ProfileName);
+}
diff --git a/private/net/svcdlls/rpl/convert/rpldata.h b/private/net/svcdlls/rpl/convert/rpldata.h
new file mode 100644
index 000000000..d62c9b2e5
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/rpldata.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rpldata.h
+
+Abstract:
+
+ RPL covert global variables (declarations & definitions).
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// rplmain.c will #include this file with RPLDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef RPLDATA_ALLOCATE
+#define EXTERN
+#define INIT( _x) = _x
+#else
+#define EXTERN extern
+#define INIT( _x)
+#endif
+
+EXTERN JET_SESID SesId;
+EXTERN JET_DBID DbId;
+EXTERN JET_INSTANCE JetInstance;
+
+EXTERN CHAR G_ServiceDatabaseA[ MAX_PATH ];
+EXTERN WCHAR G_ServiceDirectoryW[ MAX_PATH ];
+
+#ifdef RPL_DEBUG
+EXTERN int DebugLevel;
+#endif
+
diff --git a/private/net/svcdlls/rpl/convert/rplmain.c b/private/net/svcdlls/rpl/convert/rplmain.c
new file mode 100644
index 000000000..a2643e0b6
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/rplmain.c
@@ -0,0 +1,740 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplmain.c
+
+Abstract:
+
+ Converts old style (OS/2) files RPL.MAP & RPLMGR.INI
+ into a new style database to be used with NT rpl server.
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+#include <shellapi.h>
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "wksta.h"
+#include "adapter.h"
+#include "vendor.h"
+
+#define COMMA_CHAR L','
+PWCHAR RplMapFile = L"rpl.map";
+WCHAR RG_NulWchar = 0; // used for empty entries in rpl.map
+//
+// RPL.MAP defines
+//
+#define MAX_RPL_MAP_FILE_SIZE 0xFFFE // BUGBUG some arbitrary value
+typedef enum _MAP_RECORD_TYPE {
+ RecordTypeComment = 0, // comment line in PRL.MAP file
+ RecordTypeBoot, // boot block records, documented in os/2 lm2.0 & up
+ RecordTypePcDosBoot, // boot block records, undocumented but supported
+ RecordTypeProfile, // profile record
+ RecordTypeUniqueAdapter,// wksta record containing unique adapter id
+ RecordTypeWildAdapter, // wksta record containing wildcards in adapter id
+ RecordTypeUnknown // unknown record
+} MAP_RECORD_TYPE;
+
+#define EXTENDED_BOOT_ENTRIES L"yyyyyyyyyyyy"
+#define EXTENDED_BOOT_ENTRIES2 L"RPL~Header~~"
+#define PCDOS_BOOT_ENTRIES L"xxxxxxxxxxxx"
+#define PCDOS_BOOT_ENTRIES2 L"PCDOS~Header"
+#define PROFILE_ADAPTER_ID L"000000000000" // first field of profile record
+
+
+
+#define MAX_RPL_FIELD 20 // max number of entries on rpl.map line
+
+
+
+
+BOOL StringsSpaceEqual( IN PWCHAR str1, IN PWCHAR str2 )
+/*++
+
+Routine Description:
+
+ Compare strings up to the first space or NULL character in each
+ string, returning TRUE if strings are equal, FALSE otherwise.
+
+Arguments:
+
+ str1 - first string
+ str2 - second string
+
+Return Value:
+
+ TRUE if strings are equal, FALSE otherwise.
+
+--*/
+{
+ WCHAR ch1;
+
+#ifdef NOT_YET
+ if ( str1 == NULL || str2 == NULL) {
+ str1 = (str1 != NULL) ? str1 : str2;
+ if ( str2 == NULL) {
+ return( TRUE); // both strings are NULL
+ }
+ while ( iswspace( *str1++)) {
+ NOTHING; // check if not NULL string contains spaces only
+ }
+ return( *str1 == 0);
+ }
+#endif // NOT_YET
+
+ //
+ // Compare strings until the first space or NULL character
+ // (in the first string) or until we find the first different character.
+ //
+ while ( (ch1 = *str1) && !iswspace(ch1) && ch1 == *str2) {
+ str1++, str2++;
+ }
+
+ //
+ // For strings to be equal, both characters must be NULL or space.
+ //
+ if ( (!ch1 || iswspace(ch1)) && (!(ch1 = *str2) || iswspace(ch1)))
+ {
+ return( TRUE);
+ }
+
+ return( FALSE);
+}
+
+
+BOOL IsWildAdapterName( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string may be the adapter id of a wksta group.
+
+ Adapter id must be terminated with space AND must have exactly
+ RPL_ADAPTER_NAME_LENGTH hex digits.
+
+Arguments:
+
+ Cursor - pointer to adapter id string
+
+Return Value:
+ Returns TRUE if input string may be a unique adapter id of a client,
+ else FALSE.
+
+--*/
+{
+ DWORD count = 0;
+ WCHAR ch;
+
+ for ( count = 0; (ch = *Cursor) == '?' || iswxdigit(ch); count++) {
+ Cursor++;
+ }
+
+ if ( !iswspace(ch) || count != RPL_ADAPTER_NAME_LENGTH) {
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
+
+BOOL IsAdapterName( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string may be the adapter id of a wksta.
+
+ Adapter id must be terminated with space AND must have
+ exactly RPL_ADAPTER_NAME_LENGTH hex digits
+
+Arguments:
+
+ Cursor - pointer to adapter id string
+
+Return Value:
+ Returns TRUE if input string may be a unique adapter id of a client,
+ else FALSE.
+
+--*/
+{
+ DWORD count;
+
+ for ( count = 0; iswxdigit(*Cursor); count++) {
+ Cursor++;
+ }
+
+ if ( !iswspace(*Cursor) || count != RPL_ADAPTER_NAME_LENGTH) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+MAP_RECORD_TYPE RplGetRecordType( IN PWCHAR Cursor)
+/*++
+
+Routine Description:
+
+ Returns the type of a line in RPL.MAP.
+
+Arguments:
+ Cursor - pointer to line from RPL.map file
+
+Return Value:
+ Returns type of a line in RPL.map file
+
+--*/
+{
+ // strings can be either nul or space terminated
+
+ if ( *Cursor == COMMENT_CHAR) {
+
+ return( RecordTypeComment);
+ }
+
+ if ( StringsSpaceEqual( Cursor, EXTENDED_BOOT_ENTRIES) ||
+ StringsSpaceEqual( Cursor, EXTENDED_BOOT_ENTRIES2)) {
+
+ return( RecordTypeBoot);
+ }
+
+ if ( StringsSpaceEqual( Cursor, PCDOS_BOOT_ENTRIES) ||
+ StringsSpaceEqual( Cursor, PCDOS_BOOT_ENTRIES2)) {
+
+ return( RecordTypePcDosBoot);
+ }
+
+ if ( StringsSpaceEqual( Cursor, PROFILE_ADAPTER_ID)) {
+
+ return( RecordTypeProfile);
+ }
+
+ if ( IsAdapterName( Cursor)) {
+ return( RecordTypeUniqueAdapter);
+ }
+
+ if ( IsWildAdapterName( Cursor)) {
+ return( RecordTypeWildAdapter);
+ }
+
+ return( RecordTypeUnknown);
+}
+
+
+
+PWCHAR ScanChar(
+ IN PWCHAR String,
+ IN WCHAR ch
+ )
+/*++
+
+Routine Description:
+
+ Searches for the first desired character in the string. The string
+ should be NULL terminated.
+
+Arguments:
+ String - string which is NULL terminated
+ ch - character we are searching for
+
+Return Value:
+ Pointer to the first desired character (if there is such character),
+ else pointer to the last character before terminating NULL.
+
+--*/
+{
+ while ( *String && *String != ch) {
+ String++;
+ }
+ return( String);
+}
+
+
+PWCHAR ScanNonSpace( IN PWCHAR String)
+/*++
+
+Routine Description:
+ Returns pointer to the first non space character in the string.
+ This may be pointer to NULL if string has no space char.
+
+Arguments:
+ string - NULL terminated string to search in
+
+Return Value:
+ Pointer of the first non space character in the string.
+
+--*/
+{
+ while( iswspace(*String)) { // iswspace(0) is 0
+ String++;
+ }
+ return( String);
+}
+
+
+
+PWCHAR ScanSpace( IN PWCHAR String)
+/*++
+
+Routine Description:
+
+ Returns pointer to the first space character in the string if it
+ can find a space character in the string. Else, it returns pointer
+ to zero character ('\0').
+
+Arguments:
+ String - where we are searching for space character
+
+Return Value:
+ Pointer to the first space character (if there is a space char) or
+ pointer to zero.
+
+--*/
+{
+ while ( *String && !iswspace(*String)) {
+ String++;
+ }
+ return( String);
+}
+
+
+BOOL LineToWords(
+ IN PWCHAR Cursor,
+ IN PWCHAR * Fields,
+ IN DWORD FieldsLength
+ )
+/*++
+
+Routine Description:
+ Breaks the line the words and returns the pointer to a word (string) table.
+
+ Note that we do special line parsing when current table index is 9, 10
+ or 11. E.g. field ",,," of RPL.map line will result in three table
+ entries instead of just one.
+
+Arguments:
+ Cursor - ptr to line string
+ Fields - pointer to string table describing fields in RPL.MAP line
+ FieldsLength - max number of entries in fields string table,
+ not counting the terminal NULL entry
+Return Value:
+ None.
+
+--*/
+{
+#define FIRST_EXTRA_MEM 9
+#define LAST_EXTRA_MEM 11
+ PWCHAR str;
+ DWORD index;
+ DWORD length;
+
+
+ for ( index = 0; index < FieldsLength; index++) {
+
+ if (*Cursor) {
+
+ //
+ // check if extra mem field(s)
+ //
+
+ if ( index >= FIRST_EXTRA_MEM && index <= LAST_EXTRA_MEM) {
+ length = ScanChar( Cursor, COMMA_CHAR) - Cursor;
+ if ( Cursor[ length] == COMMA_CHAR) {
+ length++;
+ }
+ } else {
+ length = ScanSpace( Cursor) - Cursor;
+ }
+
+ if (length == 0 || (length == 1 && *Cursor == TILDE_CHAR)) {
+
+ Fields[ index] = &RG_NulWchar;
+
+ } else {
+
+ str = Fields[ index] = GlobalAlloc( GMEM_FIXED, (length + 1) * sizeof( WCHAR));
+ if ( str == NULL) {
+ return( FALSE);
+ }
+ memcpy( str, Cursor, length * sizeof( WCHAR));
+ str[ length] = 0;
+
+ // replace all TILDE_CHAR-s in the string
+ while (*(str = ScanChar( str, TILDE_CHAR )) == TILDE_CHAR) {
+ *str = SPACE_CHAR;
+ }
+ }
+ Cursor += length;
+
+ } else {
+ Fields[ index] = &RG_NulWchar;
+ }
+
+ //
+ // jump over spaces
+ //
+
+ Cursor = ScanNonSpace( Cursor);
+ }
+
+ Fields[ index] = NULL; // terminate the string table
+ return( TRUE);
+}
+
+
+DWORD ProcessRplMap( IN LPWSTR FilePath)
+{
+ PWCHAR Cursor;
+ PWCHAR String;
+ DWORD Error;
+ LPWSTR Fields[ MAX_RPL_FIELD + 1]; // add 1 for NULL terminator
+
+ Error = BootCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = ProfileCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = WkstaCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ Error = AdapterCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+
+ String = ReadTextFile( FilePath, RplMapFile, MAX_RPL_MAP_FILE_SIZE);
+ if ( String == NULL) {
+ return( GetLastError());
+ }
+
+ for ( Cursor = GetFirstLine( String);
+ *Cursor != 0;
+ Cursor = GetNextLine( Cursor)) {
+
+ Cursor += OffsetAfterComment( Cursor);
+ switch( RplGetRecordType( Cursor)) {
+ case RecordTypePcDosBoot:
+ NOTHING;
+ break;
+ case RecordTypeBoot: // os/2 lm2.0 & higher, boot block record
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessBoot( Fields);
+ break;
+ case RecordTypeProfile:
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessProfile( Fields);
+ break;
+ case RecordTypeUniqueAdapter:
+ LineToWords( Cursor, Fields, MAX_RPL_FIELD);
+ ProcessWksta( Fields);
+ break;
+ case RecordTypeWildAdapter:
+ NOTHING;
+ break;
+ case RecordTypeComment:
+ NOTHING;
+ break;
+ default: // unexpected line
+ DbgUserBreakPoint();
+ break;
+ }
+ }
+
+ if ( GlobalFree( String) != NULL) {
+ RplAssert( TRUE, ("GlobalFree: Error=%d", GetLastError()));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+VOID GlobalInit()
+{
+#ifdef RPL_DEBUG
+ DebugLevel = 0;
+#endif
+ SesId = 0;
+ DbId = 0;
+ JetInstance = 0;
+ BootTableId = 0;
+ ConfigTableId = 0;
+ ProfileTableId = 0;
+ WkstaTableId = 0;
+ AdapterTableId = 0;
+}
+
+
+VOID DbCleanup()
+{
+ if ( VendorTableId != 0) {
+ Call( JetCloseTable( SesId, VendorTableId));
+ }
+ if ( BootTableId != 0) {
+ Call( JetCloseTable( SesId, BootTableId));
+ }
+ if ( ConfigTableId != 0) {
+ Call( JetCloseTable( SesId, ConfigTableId));
+ }
+ if ( ProfileTableId != 0) {
+ Call( JetCloseTable( SesId, ProfileTableId));
+ }
+ if ( WkstaTableId != 0) {
+ Call( JetCloseTable( SesId, WkstaTableId));
+ }
+ if ( AdapterTableId != 0) {
+ Call( JetCloseTable( SesId, AdapterTableId));
+ }
+ if ( DbId != 0) {
+ Call( JetCloseDatabase( SesId, DbId, 0));
+ Call( JetDetachDatabase( SesId, G_ServiceDatabaseA));
+ }
+ if ( SesId != 0)
+ Call( JetEndSession( SesId, 0));
+ if ( JetInstance != 0) {
+ Call( JetTerm2( JetInstance, JET_bitTermComplete));
+ }
+}
+
+
+DWORD SetSystemParameters( VOID)
+{
+ DWORD DirectoryLengthW = sizeof(G_ServiceDirectoryW)/sizeof(WCHAR);
+ DWORD DirectoryLengthA;
+ DWORD Error = RplRegRead( NULL, NULL, NULL, G_ServiceDirectoryW, &DirectoryLengthW );
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( !WideCharToMultiByte( CP_OEMCP,
+ 0,
+ G_ServiceDirectoryW,
+ DirectoryLengthW,
+ G_ServiceDatabaseA,
+ sizeof(G_ServiceDatabaseA)/sizeof(CHAR),
+ NULL,
+ NULL ) ) {
+ return( GetLastError() );
+ }
+ DirectoryLengthA = strlen(G_ServiceDatabaseA);
+ if ( DirectoryLengthA + wcslen(RPL_SERVICE_DATABASE_W) > MAX_PATH
+ || DirectoryLengthA + strlen("temp.tmp") > MAX_PATH
+ || DirectoryLengthA + wcslen(RPL_SYSTEM_DATABASE_W) > MAX_PATH) {
+ return( NERR_RplBadRegistry);
+ }
+
+#ifdef __JET500
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramSystemPath, 0, G_ServiceDatabaseA));
+#else
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, RPL_SYSTEM_DATABASE );
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramSysDbPath, 0, G_ServiceDatabaseA));
+#endif
+
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, "temp.tmp" );
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramTempPath, 0, G_ServiceDatabaseA));
+
+ strcpy( G_ServiceDatabaseA + DirectoryLengthA, RPL_SERVICE_DATABASE );
+
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxBuffers, 250, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxOpenTables, 30, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxOpenTableIndexes, 105, NULL))
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxCursors, 100, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxSessions, 10, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxVerPages, 64, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFilePath, 0, "."));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFileSize, 1000, NULL));
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramBaseName, 0, "j50"));
+#else
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallM( JetSetSystemParameter( &JetInstance, 0, JET_paramLogFlushThreshold, 10, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD CheckFile(
+ PWCHAR FilePath,
+ PWCHAR FileName
+ )
+{
+ DWORD Error = NO_ERROR;
+ WCHAR CompleteFilePath[ MAX_PATH+1];
+ PWCHAR UseFilePath = FileName;
+ HANDLE FindHandle;
+ WIN32_FIND_DATA FindData;
+
+ CompleteFilePath[0] = L'\0';
+ if ( FilePath != NULL && lstrlenW(FilePath) > 0) {
+ lstrcpyW( CompleteFilePath, FilePath);
+ if ( CompleteFilePath[ lstrlenW(CompleteFilePath)-1 ] != L'\\') {
+ lstrcatW( CompleteFilePath, L"\\");
+ }
+ if ( FileName != NULL) {
+ lstrcatW( CompleteFilePath, FileName );
+ }
+ UseFilePath = CompleteFilePath;
+ }
+ FindHandle = FindFirstFile( UseFilePath, &FindData);
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ Error = GetLastError();
+ }
+ else
+ {
+ FindClose( FindHandle );
+ }
+
+ return(Error);
+}
+
+
+DWORD _CRTAPI1 main(
+ int argc,
+ CHAR ** charArgv
+ )
+{
+ DWORD Error;
+ JET_ERR JetError;
+ WCHAR ** argv;
+ PWCHAR SourceFilePath;
+ BOOL SpecificDirectory = FALSE;
+ PWCHAR ThisDirectory = L".\\";
+ BOOL RplsvcMdbExists = FALSE;
+
+ GlobalInit();
+ Error = SetSystemParameters();
+ if ( Error != NO_ERROR) {
+ goto SkipCleanup;
+ }
+
+ // Find files rpl.map and rplmgr.ini
+ argv = CommandLineToArgvW( GetCommandLineW(), &argc);
+ if ( argv != NULL && argc > 1) {
+ SourceFilePath = argv[ 1];
+ SpecificDirectory = TRUE;
+ } else {
+ SourceFilePath = G_ServiceDirectoryW;
+ }
+ if ( (Error = CheckFile(SourceFilePath, RplMapFile)) != NO_ERROR
+ || (Error = CheckFile(SourceFilePath, L"rplmgr.ini")) != NO_ERROR )
+ {
+ if ( (!SpecificDirectory)
+ && (Error = CheckFile(ThisDirectory, RplMapFile)) == NO_ERROR
+ && (Error = CheckFile(ThisDirectory, L"rplmgr.ini")) == NO_ERROR )
+ {
+ SourceFilePath = ThisDirectory;
+ }
+ }
+ if (Error != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_NoMAP_INI);
+ goto SkipCleanup;
+ }
+
+#ifndef __JET500
+ // find file system.mdb
+ if ( (Error = CheckFile(G_ServiceDirectoryW, RPL_SYSTEM_DATABASE_W))
+ != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_NoSystemMdb);
+ goto SkipCleanup;
+ }
+#endif
+
+ // check for file rplsvc.mdb
+ RplsvcMdbExists = ((Error = CheckFile(G_ServiceDirectoryW,
+ RPL_SERVICE_DATABASE_W))
+ == NO_ERROR);
+ if (RplsvcMdbExists) {
+ RplPrintf0( RPLI_CVT_RplsvcMdbExists);
+#ifndef RPL_DEBUG
+ goto SkipCleanup;
+#endif
+ }
+
+ JetError = JetInit( &JetInstance);
+ if ( JetError == JET_errInvalidPath) {
+ RplPrintf0( RPLI_CVT_NoMDB);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ } else if ( JetError == JET_errFileNotFound) {
+ RplPrintf0( RPLI_CVT_StopService);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ } else if ( JetError < 0) {
+ RplPrintf0( RPLI_CVT_InitError);
+ Error = MapJetError( Error);
+ goto SkipCleanup;
+ }
+ CallJ( JetBeginSession( JetInstance, &SesId, "admin", ""));
+ JetError = JetCreateDatabase( SesId, G_ServiceDatabaseA, NULL, &DbId, JET_bitDbSingleExclusive);
+ if ( JetError == JET_errDatabaseDuplicate) {
+#ifndef RPL_DEBUG
+ RplPrintf0( RPLI_CVT_OldSystemMdb);
+#endif
+ Error = MapJetError( Error);
+#ifdef RPL_DEBUG
+ if (RplsvcMdbExists) {
+ CallJ( JetAttachDatabase( SesId, G_ServiceDatabaseA, 0));
+ CallJ( JetOpenDatabase( SesId, G_ServiceDatabaseA, NULL, &DbId, JET_bitDbExclusive));
+ BootListTable();
+ ConfigListTable();
+ ProfileListTable();
+ WkstaListTable();
+ }
+#endif
+ } else if ( JetError == JET_errSuccess) {
+ Error = VendorCreateTable();
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ Error = ProcessRplMap( SourceFilePath);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ Error = ProcessRplmgrIni( SourceFilePath);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ConfigPruneTable();
+ ProfilePruneTable();
+ WkstaPruneTable();
+ } else {
+ RplAssert( TRUE, ("CreateDatabase: JetError=%d", JetError));
+ Error = MapJetError( JetError);
+ }
+
+cleanup:
+ DbCleanup();
+
+SkipCleanup:
+ if ( Error != NO_ERROR) {
+ RplPrintf0( RPLI_CVT_USAGE);
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/sources b/private/net/svcdlls/rpl/convert/sources
new file mode 100644
index 000000000..2c565a1ea
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/sources
@@ -0,0 +1,51 @@
+MAJORCOMP=net
+MINORCOMP=rplcnv
+
+TARGETPATH=obj
+TARGETNAME=rplcnv
+TARGETTYPE=PROGRAM
+NTTARGETFILE0=nlstxt.h msg00001.bin
+
+USE_CRTDLL=1
+
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\gdi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+WARNING_LEVEL=-W4
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplmain.c \
+ vendor.c \
+ adapter.c \
+ boot.c \
+ config.c \
+ profile.c \
+ wksta.c \
+ library.c \
+ debug.c \
+ convert.rc
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
diff --git a/private/net/svcdlls/rpl/convert/vendor.c b/private/net/svcdlls/rpl/convert/vendor.c
new file mode 100644
index 000000000..7e83f4491
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/vendor.c
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) 1987-1994 Microsoft Corporation
+
+Module Name:
+
+ vendor.c
+
+Abstract:
+
+ Creates boot block table (server record table). Parses old style
+ RPL.MAP server records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 16 - March - 1994
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#define RPLVENDOR_ALLOCATE
+#include "vendor.h"
+#undef RPLVENDOR_ALLOCATE
+
+RPL_VENDOR_INFO_0 VendorInfoTable[]
+ = {
+ { L"00001B", L"Novell (NE1000, NE2000)"},
+ { L"00004B", L"Nokia/ICL (EtherTeam 16)"},
+ { L"000062", L"3Com 3Station"},
+ { L"0000C0", L"Western Digital/SMC (Ethernet)"},
+ { L"0000F6", L"Madge Smart Ringnodes"},
+ { L"0020AF", L"3Com (Elnk II, Elnk III, Tokenlink III)"},
+ { L"004033", L"NE2000-compatible"},
+ { L"004095", L"NE2000-compatible"},
+ { L"00608C", L"3Com (Elnk 16, Elnk II, Elnk/MC, Elnk III)"},
+ { L"00AA00", L"Intel (EtherExpress 16, EtherExpress PRO)"},
+ { L"020701", L"Racal Interlan (NI6510, NI5210)"},
+ { L"02608C", L"3Com (3Station, Elnk, Elnk II, Elnk Plus, Elnk/MC)"},
+ { L"080009", L"HP (EtherTwist, AM2100)"},
+ { L"08005A", L"IBM (Token Ring, Ethernet)"},
+ { L"10005A", L"IBM (Token Ring, Ethernet)"},
+ { L"42608C", L"3Com (Tokenlink)"}
+};
+#define VENDOR_INFO_TABLE_LENGTH (sizeof(VendorInfoTable)/sizeof(VendorInfoTable[0]))
+
+
+VOID ProcessVendor(
+ IN PWCHAR VendorName,
+ IN PWCHAR VendorComment
+ )
+{
+ DWORD Flags;
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ RplAssert( TRUE, ("Invalid VendorName = %ws", VendorName));
+ return;
+ }
+
+ Call( JetPrepareUpdate( SesId, VendorTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_VendorName].ColumnId, VendorName,
+ (wcslen( VendorName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId, VendorComment,
+ (wcslen( VendorComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Flags = 0;
+ Call( JetSetColumn( SesId, VendorTableId,
+ VendorTable[ VENDOR_Flags].ColumnId, &Flags,
+ sizeof( Flags), 0, NULL));
+
+ Call( JetUpdate( SesId, VendorTableId, NULL, 0, NULL));
+}
+
+
+
+DWORD VendorCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, VENDOR_TABLE_NAME,
+ VENDOR_TABLE_PAGE_COUNT, VENDOR_TABLE_DENSITY, &VendorTableId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateTable failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < VENDOR_TABLE_LENGTH; index++ ) {
+
+ ColumnDef.coltyp = VendorTable[ index].ColumnType;
+
+ JetError = JetAddColumn( SesId, VendorTableId,
+ VendorTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &VendorTable[ index].ColumnId);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("AddColumn( %s) failed err=%d", VendorTable[ index].ColumnName, JetError));
+ return( MapJetError( JetError));
+ }
+ }
+
+ Offset = AddKey( IndexKey, '+', VendorTable[ VENDOR_VendorName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, VendorTableId, VENDOR_INDEX_VendorName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ for ( index = 0; index < VENDOR_INFO_TABLE_LENGTH; index++) {
+ ProcessVendor( VendorInfoTable[ index].VendorName,
+ VendorInfoTable[ index].VendorComment);
+ }
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/convert/wksta.c b/private/net/svcdlls/rpl/convert/wksta.c
new file mode 100644
index 000000000..96e45683a
--- /dev/null
+++ b/private/net/svcdlls/rpl/convert/wksta.c
@@ -0,0 +1,565 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ wksta.c
+
+Abstract:
+
+ Creates wksta table. Parses old style RPL.MAP unique adapter id
+ workstation records and creates corresponding entries in jet
+ database table.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "winsock.h" // for inet_addr()
+#define RPLWKSTA_ALLOCATE
+#include "wksta.h"
+#undef RPLWKSTA_ALLOCATE
+
+
+DWORD WkstaCreateTable( VOID)
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( SesId, DbId, WKSTA_TABLE_NAME,
+ WKSTA_TABLE_PAGE_COUNT, WKSTA_TABLE_DENSITY, &WkstaTableId);
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+ ColumnDef.grbit = 0; // variable length binary and text data.
+
+ for ( index = 0; index < WKSTA_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = WkstaTable[ index].ColumnType;
+
+ CallM( JetAddColumn( SesId, WkstaTableId,
+ WkstaTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &WkstaTable[ index].ColumnId));
+ }
+
+ //
+ // For now, the only reason we define these indices is to make sure
+ // wksta records have different AdapterName-s and different WkstaName-s.
+ // BUGBUG We could perhaps do this for TCP/IP address field as well.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_AdapterName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_AdapterName,
+ JET_bitIndexPrimary, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_WkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +ProfileName+WkstaName index is used to enumerate all wkstas attached
+ // to a given profile.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_ProfileName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ //
+ // +BootName+WkstaName index is used to find if a there is a
+ // wksta record for a given boot record.
+ //
+ Offset = AddKey( IndexKey, '+', WkstaTable[ WKSTA_BootName].ColumnName);
+ Offset += AddKey( IndexKey + Offset, '+', WkstaTable[ WKSTA_WkstaName].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ JetError = JetCreateIndex( SesId, WkstaTableId, WKSTA_INDEX_BootNameWkstaName,
+ JET_bitIndexUnique, IndexKey, Offset, 50);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("CreateIndex failed err=%d", JetError));
+ return( MapJetError( JetError));
+ }
+
+ return( ERROR_SUCCESS);
+}
+
+
+DWORD TcpIpAddressToDword( IN PWCHAR UnicodeString)
+{
+ CHAR DbcsString[ 20];
+ DWORD ByteCount;
+
+ if ( *UnicodeString == 0) {
+ //
+ // WideCharToMultiByte() would convert this string successfully,
+ // returning ByteCount equal to 1 and DbcsString[ 0] equal to 0.
+ // And inet_addr() would then return 0, a valid TCP/IP address.
+ //
+ return( INADDR_NONE);
+ }
+ ByteCount = WideCharToMultiByte( // counts the terminating null byte
+ CP_OEMCP, // RonaldM confirms inet_addr() wants OEM string
+ 0,
+ UnicodeString,
+ -1,
+ DbcsString, // dbcs string
+ sizeof( DbcsString),
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ return( INADDR_NONE); // failed to convert to DBCS
+ }
+ //
+ // Convert the string to network byte order, then to host byte order.
+ //
+ return( (DWORD)ntohl( inet_addr( DbcsString)));
+}
+
+
+VOID ProcessWksta( PWCHAR * Fields)
+/*++
+ We need to get from here whether wksta record is PERSONAL or SHARED.
+--*/
+{
+//
+// Wksta record (computer record) defines.
+//
+#define WKSTA_DISABLED_CH L'D'
+#define WKSTA_ENABLED_CH L'R'
+
+//
+// WKSTA_SERVER_INDEX is an index of a boot block record identifier in a string
+// table corresponding to a wksta record. This index is 0-based. Note
+// that wksta record is parsed so that field ",,," is counted as three
+// entries not as one.
+//
+#define WKSTA_AdapterName_INDEX 0 // wksta adapter id
+#define WKSTA_WKSTANAME_INDEX 1 // wksta name
+#define WKSTA_LOGONINPUT_INDEX 2 // username/password prompting policy
+#define WKSTA_FITFILE_INDEX 3 // fit file name
+#define WKSTA_SHARING_INDEX 5 // shared or personal profile
+#define WKSTA_BOOTNAME_INDEX 13 // boot block identifier
+#define WKSTA_PROFILENAME_INDEX 15 // profile name
+#define WKSTA_COMMENT_INDEX 16 // wksta comment
+#define WKSTA_TCPIPADDRESS_INDEX 17 // wksta tcpip address
+#define WKSTA_TCPIPSUBNET_INDEX 18 // wksta subnet mask
+#define WKSTA_TCPIPGATEWAY_INDEX 19 // wksta tcpip gateway address
+
+ PWCHAR WkstaComment;
+ PWCHAR WkstaName;
+ PWCHAR AdapterName;
+ PWCHAR ProfileName;
+ DWORD TcpIpAddress;
+ DWORD TcpIpSubnet;
+ DWORD TcpIpGateway;
+ PWCHAR Fit;
+ PWCHAR BootName;
+ JET_ERR JetError;
+ DWORD Flags;
+
+ Flags = 0;
+
+ WkstaName = Fields[ WKSTA_WKSTANAME_INDEX];
+ if ( !ValidName( WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ RplPrintf1( RPLI_CVT_WkstaInvalid, WkstaName);
+ RplAssert( TRUE, ("Bad wksta name"));
+ return;
+ }
+ _wcsupr( WkstaName);
+
+ AdapterName = Fields[ WKSTA_AdapterName_INDEX];
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidAdapter, WkstaName, AdapterName);
+ RplAssert( TRUE, ("Bad adapter id"));
+ return;
+ }
+ _wcsupr( AdapterName);
+
+ if ( Fields[ WKSTA_LOGONINPUT_INDEX] [1] != 0) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidLogon, WkstaName, Fields[ WKSTA_LOGONINPUT_INDEX]);
+ RplAssert( TRUE, ("Bad username/password prompting field."));
+ return;
+ }
+ switch( Fields[ WKSTA_LOGONINPUT_INDEX] [0]) {
+ case WKSTA_LOGON_INPUT_REQUIRED:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_REQUIRED;
+ break;
+ case WKSTA_LOGON_INPUT_OPTIONAL:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_OPTIONAL;
+ break;
+ case WKSTA_LOGON_INPUT_IMPOSSIBLE:
+ Flags |= WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidLogon, WkstaName, Fields[ WKSTA_LOGONINPUT_INDEX]);
+ RplAssert( TRUE, ("Bad username/password prompting field."));
+ return;
+ break;
+ }
+
+ Fit = AddFileExtension( Fields[ WKSTA_FITFILE_INDEX], L".FIT", TRUE);
+ if ( !ValidName( Fit, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidFit, WkstaName, Fields[ WKSTA_FITFILE_INDEX]);
+ RplAssert( TRUE, ("Bad fit file field %ws.", Fields[ WKSTA_FITFILE_INDEX]));
+ return;
+ }
+
+ //
+ // Find if wksta record is enabled or disabled.
+ //
+ switch( Fields[ WKSTA_SHARING_INDEX] [0]) {
+ case WKSTA_SHARING_TRUE:
+ Flags |= WKSTA_FLAGS_SHARING_TRUE;
+ break;
+ case WKSTA_SHARING_FALSE:
+ Flags |= WKSTA_FLAGS_SHARING_FALSE;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidSharing, WkstaName, Fields[ WKSTA_SHARING_INDEX]);
+ RplAssert( TRUE, ("Data sharing not properly set."));
+ return;
+ break;
+ }
+
+ //
+ // Find if wksta record is enabled or disabled.
+ //
+ switch( *Fields[ WKSTA_BOOTNAME_INDEX]) {
+ case WKSTA_ENABLED_CH:
+ break;
+ case WKSTA_DISABLED_CH:
+ RplPrintf2( RPLI_CVT_WkstaDisabledBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ return;
+ break;
+ default:
+ RplPrintf2( RPLI_CVT_WkstaInvalidBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad switch"));
+ return;
+ break;
+ }
+
+ BootName = Fields[ WKSTA_BOOTNAME_INDEX] + 1; // skip leading char
+ if ( !ValidName( BootName, RPL_MAX_STRING_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidBoot, WkstaName, Fields[ WKSTA_BOOTNAME_INDEX]);
+ RplAssert( TRUE, ("Bad boot name"));
+ return;
+ }
+
+ ProfileName = Fields[ WKSTA_PROFILENAME_INDEX];
+ if ( !ValidName( ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ RplPrintf2( RPLI_CVT_WkstaInvalidProfile, WkstaName, ProfileName);
+ RplAssert( TRUE, ("Bad profile name"));
+ return;
+ }
+ _wcsupr( ProfileName);
+
+ WkstaComment = Fields[ WKSTA_COMMENT_INDEX];
+ if ( RPL_STRING_TOO_LONG( WkstaComment)) {
+ WkstaComment[ RPL_MAX_STRING_LENGTH] = 0; // silently truncate it
+ }
+
+ TcpIpAddress = TcpIpAddressToDword( Fields[ WKSTA_TCPIPADDRESS_INDEX]);
+ TcpIpSubnet = TcpIpAddressToDword( Fields[ WKSTA_TCPIPSUBNET_INDEX]);
+ TcpIpGateway = TcpIpAddressToDword( Fields[ WKSTA_TCPIPGATEWAY_INDEX]);
+
+ //
+ // If all addresses are valid assumes this (old style) client does not
+ // want DHCP to be enabled. In other words, if any addresses is bogus
+ // this client does not loose anything by trying out DHCP.
+ //
+ if ( TcpIpAddress != INADDR_NONE && TcpIpSubnet != INADDR_NONE
+ && TcpIpGateway != -1) {
+ Flags |= WKSTA_FLAGS_DHCP_FALSE;
+ } else {
+ Flags |= WKSTA_FLAGS_DHCP_TRUE;
+ }
+
+ //
+ // Play it safe; assume user accounts are not to be deleted
+ //
+ Flags |= WKSTA_FLAGS_DELETE_FALSE;
+
+ Call( JetPrepareUpdate( SesId, WkstaTableId, JET_prepInsert));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, WkstaName,
+ (wcslen( WkstaName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaComment].ColumnId, WkstaComment,
+ (wcslen( WkstaComment) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, ProfileName,
+ (wcslen( ProfileName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, BootName,
+ (wcslen( BootName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId, Fit,
+ (wcslen( Fit) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_AdapterName].ColumnId, AdapterName,
+ (wcslen( AdapterName) + 1) * sizeof(WCHAR), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &TcpIpAddress,
+ sizeof( TcpIpAddress), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &TcpIpSubnet,
+ sizeof( TcpIpSubnet), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &TcpIpGateway,
+ sizeof( TcpIpGateway), 0, NULL));
+
+ Call( JetSetColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId, &Flags,
+ sizeof( Flags), 0, NULL));
+
+ JetError = JetUpdate( SesId, WkstaTableId, NULL, 0, NULL);
+ if ( JetError == JET_errKeyDuplicate) {
+ RplPrintf2( RPLI_CVT_WkstaDuplicateName, WkstaName, AdapterName);
+ } else if (JetError != JET_errSuccess) {
+ RplAssert( TRUE,("JetUpdate failed error = %d", JetError));
+ }
+}
+
+
+VOID WkstaPruneTable( VOID)
+/*++
+ Eliminate wksta records that do not have a corresponding profile record
+ defined or do not have a corresponding server record defined.
+--*/
+{
+
+ WCHAR Name[ 20]; // BUGBUG arbitrary size
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ JET_ERR JetError;
+
+ for ( ForJetError = JetMove( SesId, WkstaTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( SesId, WkstaTableId, JET_MoveNext, 0)) {
+
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ if ( !FindBoot( Name)) {
+ RplAssert( TRUE, ("FindBoot failed."));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ //
+ // This will eliminate workstations joined to the DEFAULT profile
+ // since the DEFAULT profile does not have its profile record in
+ // RPL.MAP. Note that default boot is not supported under NT.
+ // Instead of just deleting these workstations, we could also
+ // add the corresponding AdapterName record to the AdapterName table.
+ // This may not be worth the effort though.
+ //
+ if ( !FindProfile( Name)) {
+ if ( _wcsicmp( Name, L"DEFAULT") == 0) {
+ JetError = JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, Name,
+ sizeof( Name), &NameSize, 0, NULL);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("RetriveColumn failed err=%d", JetError));
+ } else {
+ RplPrintf1( RPLI_CVT_WkstaDefaultProfile, Name);
+ }
+ } else {
+ RplAssert( TRUE, ("FindProfile failed."));
+ }
+ Call( JetDelete( SesId, WkstaTableId));
+ continue;
+ }
+ }
+
+ //
+ // The error below is the only expected error (end of table error).
+ //
+ if ( ForJetError != JET_errNoCurrentRecord) {
+ RplAssert( TRUE, ("ForJetError = %d", ForJetError));
+ }
+}
+
+
+VOID WkstaListTable( VOID)
+{
+ ListTable( WKSTA_TABLE_NAME, WkstaTable[ WKSTA_AdapterName].ColumnName,
+ WKSTA_INDEX_WkstaName);
+}
+
+
+
+BOOL RplDbInitTable( IN PCHAR TableName, IN OUT PRPL_COLUMN_INFO Table,
+ IN DWORD TableLength, IN OUT JET_TABLEID * pTableId)
+{
+ JET_COLUMNDEF ColumnDef;
+ DWORD index;
+
+ CallB( JetOpenTable( SesId, DbId, TableName, NULL, 0,
+ JET_bitTableDenyWrite, pTableId));
+
+ for ( index = 0; index < TableLength; index++) {
+ CallB( JetGetTableColumnInfo( SesId, *pTableId, Table[ index].ColumnName,
+ &ColumnDef, sizeof( ColumnDef), JET_ColInfo));
+ Table[ index].ColumnId = ColumnDef.columnid;
+ Table[ index].ColumnType = ColumnDef.coltyp;
+ }
+ return( TRUE);
+}
+
+
+BOOL FindWksta( IN LPWSTR AdapterName)
+/*++
+ Return TRUE if it finds wksta record for input AdapterName.
+ Returns FALSE otherwise.
+
+ BUGBUG This code is inefficient. I should really make AdapterName a
+ jet currency data, but even now it is kind of stupid taking wcslen of
+ AdapterName since it is a fixed length string.
+--*/
+{
+ JET_ERR JetError;
+
+ JetError = JetSetCurrentIndex( SesId, WkstaTableId, WKSTA_INDEX_AdapterName);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( SesId, WkstaTableId, AdapterName,
+ ( wcslen( AdapterName) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplAssert( TRUE, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( SesId, WkstaTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error, do not break for this.
+ //
+ RplAssert( TRUE, ( "FindWksta( %ws) failed", AdapterName));
+ } else {
+ RplAssert( TRUE, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+#ifdef NOT_YET
+
+VOID Display( IN DWORD index)
+{
+ BYTE Buffer[ 120];
+ DWORD DataSize;
+ DWORD AddressDword;
+
+ Call( JetRetrieveColumn( SesId, WkstaTableId,
+ WkstaTable[ index].ColumnId, Buffer,
+ sizeof( Buffer), &DataSize, 0, NULL));
+
+ RplDbgPrint(( "%s = ", WkstaTable[ index].ColumnName));
+
+ switch( index) {
+ case WKSTA_WkstaName:
+ case WKSTA_WkstaComment:
+ case WKSTA_ProfileName:
+ case WKSTA_FitFile:
+ case WKSTA_BootName:
+ RplDbgPrint(( "%ws\n", Buffer));
+ break;
+ case WKSTA_TcpIpAddress:
+ case WKSTA_TcpIpSubnet:
+ case WKSTA_TcpIpGateway:
+ AddressDword = *(PDWORD)Buffer;
+ FillTcpIpString( Buffer, AddressDword);
+ RplDbgPrint(( "%s\n", Buffer));
+ break;
+ case WKSTA_Flags:
+ RplDbgPrint(( "0x%x\n", *(PDWORD)Buffer));
+ break;
+ }
+}
+
+
+BOOL ListWkstaInfo( IN PWCHAR AdapterName)
+/*++
+ Returns TRUE if it can find all the information needed to boot the client.
+ Returns FALSE otherwise.
+--*/
+{
+ if ( !RplDbInitTable( WKSTA_TABLE_NAME, WkstaTable, WKSTA_TABLE_LENGTH,
+ &WkstaTableId)) {
+ return( FALSE);
+ }
+ if ( !FindWksta( AdapterName)) {
+ RplAssert( TRUE, ("FindWksta( %ws) failed", AdapterName));
+ return( FALSE);
+ }
+ Display( WKSTA_WkstaName);
+ Display( WKSTA_WkstaComment);
+ Display( WKSTA_Flags);
+ Display( WKSTA_ProfileName);
+ Display( WKSTA_FitFile);
+ Display( WKSTA_BootName);
+ Display( WKSTA_TcpIpAddress);
+ Display( WKSTA_TcpIpSubnet);
+ Display( WKSTA_TcpIpGateway);
+ return( TRUE);
+}
+#endif
+
diff --git a/private/net/svcdlls/rpl/dirs b/private/net/svcdlls/rpl/dirs
new file mode 100644
index 000000000..4d6610aed
--- /dev/null
+++ b/private/net/svcdlls/rpl/dirs
@@ -0,0 +1,33 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= \
+ client \
+ lib \
+ dll \
+ server \
+ convert \
+ command
+
+
+OPTIONAL_DIRS=rplinst
diff --git a/private/net/svcdlls/rpl/dll/buffer.c b/private/net/svcdlls/rpl/dll/buffer.c
new file mode 100644
index 000000000..0d7336dcb
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/buffer.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ buffer.c (renamed from rpl2_bfr.c)
+
+Abstract:
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+BOOL RplDlcBufferFree(
+ BYTE free_type,
+ PADAPTER_INFO pAdapterInfo
+ )
+/*++
+
+Routine Description:
+ Frees the DLC buffer. Can be called from more than one thread.
+
+Arguments:
+ free_type - do we free SFR or FIND receive buffer
+ pAdapterInfo - pointer to adapter specific info
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+--*/
+
+{
+ DWORD status;
+ PLLC_CCB pBadCcb; // pointer for AcsLan
+ PLLC_CCB pCcb;
+ PLLC_BUFFER_FREE_PARMS pBufferFreeParms;
+ HANDLE Event;
+ WORD usStationId;
+ PLLC_BUFFER pBuffer;
+
+ if (free_type == FIND_FREE) {
+ pCcb= &pAdapterInfo->find_bf_ccb;
+ pBufferFreeParms = &pAdapterInfo->find_bf_ccbpt;
+ pBuffer = pAdapterInfo->u1.r1.pFirstBuffer;
+ usStationId = pAdapterInfo->u1.r1.usStationId;
+ Event = pAdapterInfo->findsem;
+ } else {
+ pCcb= &pAdapterInfo->sfr_bf_ccb;
+ pBufferFreeParms = &pAdapterInfo->sfr_bf_ccbpt;
+ pBuffer = pAdapterInfo->u3.r1.Type.Event.pReceivedFrame;
+ usStationId = pAdapterInfo->u3.r1.usStationId;
+ Event = pAdapterInfo->getdata_sem;
+ }
+
+ //
+ // clear the blocks first
+ //
+ memset( (PVOID)pCcb, '\0', sizeof(LLC_CCB));
+ memset( (PVOID)pBufferFreeParms, '\0', sizeof(LLC_BUFFER_FREE_PARMS));
+
+ pCcb->uchAdapterNumber = pAdapterInfo->adapter_number;
+ pCcb->uchDlcCommand = LLC_BUFFER_FREE;
+ pCcb->hCompletionEvent = Event;
+ pCcb->u.pParameterTable = (PLLC_PARMS)pBufferFreeParms;
+
+ pBufferFreeParms->pFirstBuffer = (PLLC_XMIT_BUFFER)pBuffer;
+ if ( pBuffer == NULL) {
+ RplDump( ++RG_Assert, ("pBuffer=0x%x", pBuffer));
+ return( TRUE); // a workaround for free build until the bug is found BUGBUG
+ }
+
+ if ( ResetEvent( Event) == FALSE) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert, ( "status=0x%x", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( pCcb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( Event);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert, ( "status=0x%x", status));
+ RplDlcReportEvent( status, LLC_BUFFER_FREE);
+ (VOID)SetEvent( Event);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( Event, 3000L);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pCcb->uchDlcStatus)) {
+ if (status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pCcb->uchDlcStatus, LLC_BUFFER_FREE);
+ }
+ RplDump( ++RG_Assert, ( "status=%d, pCcb=0x%x, DlcStatus=0x%x",
+ status, pCcb, pCcb->uchDlcStatus));
+ return( FALSE);
+ }
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/dll/emulator.c b/private/net/svcdlls/rpl/dll/emulator.c
new file mode 100644
index 000000000..6f0110037
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/emulator.c
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ emulator.c (renamed from rplemul.c)
+
+Abstract:
+
+ Contains RPL emulator for ETHERSTART clients.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 08-Jun-1994 vladimv
+ Updated with lm2.2 fixes for 3Com Etherlink card.
+
+--*/
+
+#include "local.h"
+
+//
+// ripl_rom[] is the x86 code we send to ETHERSTART clients. The source
+// code is on \\deficit\lm\src\ripl\rpldll\rplemul.asm.
+//
+// BUGBUG We should really check in & compile the actual x86 assembly code
+// BUGBUG rather than collecting sniff traces of what MS Lan Manager server
+// BUGBUG sends to the client.
+//
+BYTE ripl_rom[] = {
+ 0xFA ,0xB8 ,0x90 ,0x00
+,0x8E ,0xD0 ,0xBC ,0x00 ,0x02 ,0xCD ,0x12 ,0xB1 ,0x06 ,0xD3 ,0xE0 ,0x8D ,0x0E ,0x53 ,0x0B ,0x83
+,0xC1 ,0x0F ,0x51 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0xD1 ,0xE9 ,0x2B ,0xC1 ,0x59 ,0x8E ,0xC0
+,0x8D ,0x36 ,0x35 ,0x01 ,0x8B ,0xFE ,0x2B ,0xCE ,0xE8 ,0x56 ,0x02 ,0x50 ,0xB8 ,0x35 ,0x01 ,0x50
+,0xCB ,0x0E ,0x1F ,0xB4 ,0xFB ,0xB2 ,0x80 ,0x8D ,0x1E ,0x76 ,0x04 ,0xCD ,0x13 ,0x8B ,0xF3 ,0x8D
+,0x3E ,0xA1 ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0x8B ,0xF3 ,0x8D ,0x3E ,0xDE ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0x8B
+,0xF3 ,0x8D ,0x3E ,0x09 ,0x05 ,0xA5 ,0xA5 ,0xA5 ,0xB4 ,0x03 ,0x32 ,0xFF ,0xCD ,0x10 ,0x32 ,0xD2
+,0x01 ,0x16 ,0x04 ,0x04 ,0x01 ,0x16 ,0x39 ,0x04 ,0x01 ,0x16 ,0x6E ,0x04 ,0xBE ,0xDA ,0x03 ,0x8B
+,0x16 ,0x04 ,0x04 ,0xBB ,0x24 ,0x00 ,0x90 ,0xE8 ,0x26 ,0x02 ,0xA1 ,0x04 ,0x04 ,0x05 ,0x24 ,0x00
+,0x90 ,0xA3 ,0x04 ,0x04 ,0xBE ,0xFE ,0x03 ,0x8B ,0x16 ,0x04 ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8
+,0x0E ,0x02 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0x70 ,0x04 ,0xE8 ,0xF5 ,0x01 ,0xBE ,0xBE ,0x05
+,0x8D ,0x1E ,0x42 ,0x05 ,0xE8 ,0xF2 ,0x01 ,0x73 ,0x13 ,0x0B ,0xC0 ,0x74 ,0x03 ,0xE9 ,0x8D ,0x00
+,0xBE ,0xFE ,0x03 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0xFE ,0x01 ,0xEB ,0xC8 ,0x8D ,0x36 ,0x72 ,0x05
+,0x8D ,0x3E ,0xD8 ,0x04 ,0xA5 ,0xA5 ,0xA5 ,0xBE ,0x06 ,0x04 ,0x8B ,0x16 ,0x39 ,0x04 ,0xBB ,0x2F
+,0x00 ,0x90 ,0xE8 ,0xCB ,0x01 ,0xBE ,0x3B ,0x04 ,0x8B ,0x16 ,0x6E ,0x04 ,0xBB ,0x2D ,0x00 ,0x90
+,0xE8 ,0xBD ,0x01 ,0xA1 ,0x39 ,0x04 ,0x05 ,0x2F ,0x00 ,0x90 ,0xA3 ,0x39 ,0x04 ,0xA1 ,0x6E ,0x04
+,0x05 ,0x2D ,0x00 ,0x90 ,0xA3 ,0x6E ,0x04 ,0xB9 ,0x50 ,0x00 ,0xBE ,0x35 ,0x04 ,0x8B ,0x16 ,0x39
+,0x04 ,0xBB ,0x04 ,0x00 ,0x90 ,0xE8 ,0x98 ,0x01 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0xD8 ,0x04
+,0xE8 ,0x7F ,0x01 ,0xBE ,0x68 ,0x04 ,0x8B ,0x16 ,0x6E ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0x7F
+,0x01 ,0xBE ,0xBE ,0x05 ,0x8D ,0x1E ,0x8D ,0x05 ,0xE8 ,0x6E ,0x01 ,0x73 ,0x28 ,0x0B ,0xC0 ,0x75
+,0x0C ,0xBE ,0x35 ,0x04 ,0xBB ,0x04 ,0x00 ,0x90 ,0xE8 ,0x7D ,0x01 ,0xE2 ,0xBD ,0xE8 ,0xF6 ,0x00
+,0xB8 ,0x40 ,0x00 ,0x8E ,0xD8 ,0xC7 ,0x06 ,0x72 ,0x00 ,0x34 ,0x12 ,0xEA ,0x00 ,0x00 ,0xFF ,0xFF
+,0xB9 ,0x50 ,0x00 ,0xEB ,0xBE ,0x80 ,0x3E ,0x9B ,0x05 ,0xFC ,0x75 ,0xF4 ,0x81 ,0x3E ,0xA0 ,0x05
+,0x00 ,0x20 ,0x75 ,0xEC ,0xA1 ,0xF3 ,0x04 ,0x39 ,0x06 ,0xA8 ,0x05 ,0x74 ,0x02 ,0xEB ,0x88 ,0x8B
+,0x16 ,0xF1 ,0x04 ,0x39 ,0x16 ,0xA6 ,0x05 ,0x74 ,0x03 ,0xE9 ,0x7B ,0xFF ,0x86 ,0xC4 ,0x86 ,0xD6
+,0x40 ,0x83 ,0xD2 ,0x00 ,0x86 ,0xC4 ,0x86 ,0xD6 ,0xA3 ,0xF3 ,0x04 ,0x89 ,0x16 ,0xF1 ,0x04 ,0xBE
+,0x68 ,0x04 ,0xBB ,0x06 ,0x00 ,0x90 ,0xE8 ,0x1F ,0x01 ,0x8A ,0x1E ,0xB6 ,0x05 ,0xF6 ,0xC3 ,0x10
+,0x74 ,0x0D ,0x53 ,0xBE ,0x68 ,0x00 ,0x90 ,0x8D ,0x1E ,0xD8 ,0x04 ,0xE8 ,0xE4 ,0x00 ,0x5B ,0xF6
+,0xC3 ,0x20 ,0x74 ,0x10 ,0xA1 ,0xB0 ,0x05 ,0x86 ,0xE0 ,0xA3 ,0x4B ,0x0B ,0xA1 ,0xAE ,0x05 ,0x86
+,0xE0 ,0xA3 ,0x4D ,0x0B ,0x8B ,0x0E ,0xB7 ,0x05 ,0x86 ,0xCD ,0x83 ,0xE9 ,0x04 ,0x0B ,0xC9 ,0x74
+,0x37 ,0x33 ,0xD2 ,0xA1 ,0x4B ,0x0B ,0xBF ,0x10 ,0x00 ,0xF7 ,0xF7 ,0x8B ,0xFA ,0x03 ,0x06 ,0x4D
+,0x0B ,0x75 ,0x09 ,0x81 ,0xFA ,0x00 ,0x0C ,0x73 ,0x03 ,0xE9 ,0x51 ,0xFF ,0x03 ,0xD1 ,0x89 ,0x16
+,0x4B ,0x0B ,0xA3 ,0x4D ,0x0B ,0x3D ,0x00 ,0xA0 ,0x72 ,0x03 ,0xE9 ,0x40
+ ,0xFF ,0x06 ,0x8E ,0xC0
+,0x8D ,0x36 ,0xBB ,0x05 ,0xE8 ,0x7A ,0x00 ,0x07 ,0xF6 ,0xC3 ,0x80 ,0x75 ,0x03 ,0xE9 ,0x40 ,0xFF
+,0xA1 ,0x4F ,0x0B ,0x8B ,0x0E ,0x51 ,0x0B ,0xF6 ,0xC3 ,0x40 ,0x74 ,0x0B ,0xA1 ,0xB4 ,0x05 ,0x86
+,0xC4 ,0x8B ,0x0E ,0xB2 ,0x05 ,0x86 ,0xCD ,0x33 ,0xD2 ,0xBB ,0x10 ,0x00 ,0xF7 ,0xF3 ,0x03 ,0xC1
+,0x50 ,0x52 ,0xE8 ,0x01 ,0x00 ,0xCB ,0x06 ,0x1E ,0x33 ,0xC0 ,0x8E ,0xC0 ,0xB8 ,0x70 ,0x00 ,0x8E
+,0xD8 ,0xA1 ,0x02 ,0x00 ,0x0B ,0x06 ,0x04 ,0x00 ,0x75 ,0x12 ,0x26 ,0xA1 ,0x00 ,0x01 ,0x26 ,0xA3
+,0x4C ,0x00 ,0x26 ,0xA1 ,0x02 ,0x01 ,0x26 ,0xA3 ,0x4E ,0x00 ,0xEB ,0x0E ,0xA1 ,0x02 ,0x00 ,0x26
+,0xA3 ,0x4C ,0x00 ,0xA1 ,0x04 ,0x00 ,0x26 ,0xA3 ,0x4E ,0x00 ,0xA1 ,0x06 ,0x00 ,0x26 ,0xA3 ,0x64
+,0x00 ,0xA1 ,0x08 ,0x00 ,0x26 ,0xA3 ,0x66 ,0x00 ,0x26 ,0xC6 ,0x06 ,0xD0 ,0x04 ,0x01 ,0x1F ,0x07
+,0xC3 ,0xF7 ,0xC6 ,0x01 ,0x00 ,0x74 ,0x01 ,0xA4 ,0xD1 ,0xE9 ,0x9C ,0xF3 ,0xA5 ,0x9D ,0x73 ,0x01
+,0xA4 ,0xC3 ,0xB4 ,0xFE ,0xB2 ,0x80 ,0xCD ,0x13 ,0xC3 ,0xB4 ,0xFF ,0xB2 ,0x80 ,0xCD ,0x13 ,0xC3
+,0x50 ,0x51 ,0x53 ,0x8B ,0xCB ,0x32 ,0xFF ,0xB4 ,0x02 ,0xCD ,0x10 ,0xAC ,0x33 ,0xDB ,0xB4 ,0x0E
+,0xCD ,0x10 ,0xE2 ,0xF7 ,0x5B ,0x59 ,0x58 ,0xC3 ,0x50 ,0x51 ,0x8D ,0x70 ,0xFF ,0x8B ,0xCB ,0x8A
+,0x04 ,0x3C ,0x39 ,0x7C ,0x0A ,0xB0 ,0x30 ,0x88 ,0x04 ,0x4E ,0xE2 ,0xF3 ,0xEB ,0x05 ,0x90 ,0xFE
+,0xC0 ,0x88 ,0x04 ,0x59 ,0x58 ,0xC3 ,0x53 ,0x65 ,0x61 ,0x72 ,0x63 ,0x68 ,0x69 ,0x6E ,0x67 ,0x20
+,0x66 ,0x6F ,0x72 ,0x20 ,0x52 ,0x50 ,0x4C ,0x20 ,0x53 ,0x65 ,0x72 ,0x76 ,0x65 ,0x72 ,0x2C ,0x20
+,0x52 ,0x65 ,0x74 ,0x72 ,0x69 ,0x65 ,0x73 ,0x20 ,0x3D ,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30
+,0x00 ,0x01 ,0x53 ,0x65 ,0x6E ,0x64 ,0x69 ,0x6E ,0x67 ,0x20 ,0x46 ,0x69 ,0x6C ,0x65 ,0x20 ,0x52
+,0x65 ,0x71 ,0x75 ,0x65 ,0x73 ,0x74 ,0x73 ,0x20 ,0x74 ,0x6F ,0x20 ,0x52 ,0x50 ,0x4C ,0x20 ,0x53
+,0x65 ,0x72 ,0x76 ,0x65 ,0x72 ,0x2C ,0x20 ,0x52 ,0x65 ,0x74 ,0x72 ,0x69 ,0x65 ,0x73 ,0x20 ,0x3D
+,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x00 ,0x03 ,0x57 ,0x61 ,0x69 ,0x74 ,0x69 ,0x6E ,0x67 ,0x20 ,0x66
+,0x6F ,0x72 ,0x20 ,0x44 ,0x61 ,0x74 ,0x61 ,0x20 ,0x46 ,0x72 ,0x61 ,0x6D ,0x65 ,0x20 ,0x77 ,0x69
+,0x74 ,0x68 ,0x20 ,0x53 ,0x65 ,0x71 ,0x75 ,0x65 ,0x6E ,0x63 ,0x65 ,0x20 ,0x4E ,0x75 ,0x6D ,0x62
+,0x65 ,0x72 ,0x3A ,0x20 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x30 ,0x00 ,0x04 ,0x03 ,0x00 ,0x02 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x5A ,0xFC ,0xFC ,0x03 ,0x00 ,0x57 ,0x00
+,0x01 ,0x00 ,0x08 ,0x40 ,0x03 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x10 ,0x00 ,0x08 ,0x00 ,0x06 ,0x40
+,0x09 ,0x05 ,0xBE ,0x00 ,0x06 ,0x40 ,0x0A ,0x00 ,0x01 ,0x00 ,0x0A ,0x40 ,0x06 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x05 ,0x40 ,0x07 ,0xFC ,0x00 ,0x2C ,0x00 ,0x04 ,0x00 ,0x24 ,0xC0 ,0x05
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x04 ,0x40 ,0x13 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x5A ,0xF8 ,0xFC ,0x03 ,0x00 ,0x57 ,0x00 ,0x10 ,0x00 ,0x08 ,0x40 ,0x11 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x10 ,0x00 ,0x08 ,0x00 ,0x06 ,0x40 ,0x09 ,0x05 ,0xBE ,0x00
+ ,0x06 ,0x40 ,0x0A ,0x00
+,0x01 ,0x00 ,0x0A ,0x40 ,0x06 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x05 ,0x40 ,0x07 ,0xFC
+,0x00 ,0x2C ,0x00 ,0x04 ,0x00 ,0x24 ,0xC0 ,0x05 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00
+,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x04 ,0x40 ,0x13
+};
+WORD sizeof_ripl_rom = sizeof( ripl_rom);
+
diff --git a/private/net/svcdlls/rpl/dll/ether.c b/private/net/svcdlls/rpl/dll/ether.c
new file mode 100644
index 000000000..bd8cb05f7
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/ether.c
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ ether.c
+
+Abstract:
+
+ Contains EtherStartThread()
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 16-Aug-1995 jonn
+ Extensions to allow EtherStart clients bridged to FDDI
+ to boot from FDDI adapters. FDDI adapters use a SAP station
+ rather than a DIRECT station.
+
+--*/
+
+#include "local.h"
+#include <jet.h> // need to include because rpllib.h depends on JET
+#include <rpllib.h> // RplReportEvent()
+
+//
+// The following are XNS packet definitions used to crack EtherStart
+// packets.
+//
+
+#include <packon.h> // pack EtherStart structures
+
+struct sockaddr_ns {
+ DWORD net;
+ BYTE host[ NODE_ADDRESS_LENGTH];
+ WORD socket;
+};
+
+#define ETHERSTART_SOCKET 0x0104 // After swapping bytes
+
+#define SNAP_SAP 0xAA; // SAP used for SNAP packets
+
+struct _IDP {
+ WORD chksum;
+ WORD len;
+ BYTE xport_cntl;
+ BYTE packet_type;
+ struct sockaddr_ns dest;
+ struct sockaddr_ns src;
+};
+
+#define XNS_NO_CHKSUM 0xffff // XNS checksum
+#define PEX_TYPE 0x4 // packet exchange type
+
+struct _PEX {
+ DWORD pex_id;
+ WORD pex_client;
+};
+
+#define ETHERSERIES_CLIENT 0x0080 // After swapping bytes
+
+struct _ETHERSTART {
+ WORD ethershare_ver;
+ BYTE unit;
+ BYTE fill;
+ WORD block;
+ BYTE func;
+ BYTE error;
+ WORD bytes;
+};
+
+#define ETHERSHARE_VER 0
+#define FUNC_RESPONSE 0x80
+#define FUNC_READFILEREQ 0x20
+#define FUNC_READFILERESP (FUNC_READFILEREQ | FUNC_RESPONSE)
+
+typedef struct _ETHERSTART_REQ { // EtherStart Read File Request
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE filename[64];
+ WORD start;
+ WORD count;
+} ETHERSTART_REQ;
+
+typedef struct _ETHERSTART_RESP { // EtherStart Read File Response
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE data[0x200];
+} ETHERSTART_RESP;
+
+typedef struct _SNAP_FRAME { // SNAP packet
+ struct {
+ BYTE orgcode[3];
+ BYTE etype[2];
+ } header;
+ union {
+ ETHERSTART_REQ request;
+ ETHERSTART_RESP response;
+ } data;
+} SNAP_FRAME;
+
+//
+// RECEIVE case: DLC oddity in MS-unique DIRECT extension.
+// In case of a direct open auchLanHeader[] returned by DLC contains
+// LAN_HEADER structure without physical control fields. (I.e.
+// it contains LAN_HEADER_TOO structure defined below.). Therefore
+// 3Com 3Station address begins at offset 6 instead of at offset 8.
+//
+#define RPLDLC_DIRECT_SOURCE_OFFSET 6 // WARNING not 8
+#define RPLDLC_SAP_SOURCE_OFFSET 8
+
+//
+// TRANSMIT case: possible DLC bug
+// If LAN_HEADER is used then DLC sends only the first 4 bytes of
+// destination address ( and the first byte of destination address is
+// at offset 2 instead of 0). But if we use LAN_HEADER_TOO then this
+// problem goes away.
+//
+typedef struct _LAN_HEADER_TOO {
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER_TOO; // BUGBUG not LAN_HEADER due to a DLC bug
+
+#include <packoff.h> // restore default packing (done with EtherStart structures)
+
+
+
+#ifdef RPL_DEBUG
+VOID RplCopy( PVOID destination, PVOID source, DWORD length) {
+ memcpy( destination, source, length);
+}
+#else
+#define RplCopy( _a0, _a1, _a2) memcpy( _a0, _a1, _a2)
+#endif
+
+
+BOOL EtherAcsLan(
+ PADAPTER_INFO pAdapterInfo,
+ PLLC_CCB pCcb,
+ BYTE Command
+ )
+{
+ PLLC_CCB pBadCcb;
+ DWORD status;
+ DWORD retry_count;
+
+ pCcb->uchDlcCommand = Command;
+ retry_count = 0;
+
+transmit_retry:
+ status = AcsLan( pCcb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "status=0x%x", status));
+ RplDlcReportEvent( status, Command);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pCcb->hCompletionEvent, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != WAIT_OBJECT_0 || pCcb->uchDlcStatus) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "EtherAcslan(0x%x): retry_count=%d, status=%d, DlcStatus=0x%x",
+ Command, retry_count, status, pCcb->uchDlcStatus));
+ Sleep( 100L * retry_count); // wait for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,( "pBadCcb=0x%x status=%d DlcStatus=0x%x",
+ pBadCcb, status, pCcb->uchDlcStatus));
+ if ( status) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pCcb->uchDlcStatus, Command);
+ }
+ return( FALSE);
+ }
+
+ //
+ // Due to a DLC bug "pNext" field is trashed even on success
+ // code path. Until the bug is fix we must reinstate NULL.
+ //
+ pCcb->pNext = NULL;
+ return( TRUE);
+}
+
+
+//
+// RG_EtherFileName[] contains names of all legal boot request types.
+// We will do a case insensitive search since file name may arrive
+// either in uppercase or lowercase depending on 3Station.
+// RG_EtherFileName[] should be initialized during dll init time. BUGBUG
+//
+
+PCHAR RG_EtherFileName[] = { // names of all legal boot request types
+ "BOOTPC.COM",
+ "BOOTSTAT.COM",
+ NULL
+ };
+
+BOOL RplCrack( ETHERSTART_REQ * pEtherstartReq)
+{
+ PCHAR pFileName;
+ DWORD index;
+
+ if ( pEtherstartReq->idp.packet_type != PEX_TYPE ||
+ pEtherstartReq->pex.pex_client != ETHERSERIES_CLIENT ||
+ pEtherstartReq->es.func != FUNC_READFILEREQ) {
+ return( FALSE);
+ }
+
+ //
+ // Make sure this is a legal file request.
+ //
+ for ( index = 0, pFileName = RG_EtherFileName[ index];
+ pFileName != NULL;
+ pFileName = RG_EtherFileName[ ++index] ) {
+ if ( _stricmp( pEtherstartReq->filename, pFileName)) {
+ return( TRUE);
+ }
+ }
+ return( FALSE);
+}
+
+
+VOID EtherStartThread( POPEN_INFO pOpenInfo)
+{
+ PADAPTER_INFO pAdapterInfo;
+ LLC_CCB ccb;
+ BOOL BufferFree;
+ ETHERSTART_REQ * pEtherstartReq;
+ PRCVBUF pRcvbuf;
+ struct {
+ XMIT_BUFFER XmitBuffer; // see comment for XMIT_BUFFER
+ LAN_HEADER_TOO LanHeader;
+ } XmitQueue; // first send buffer.
+ SNAP_FRAME SnapResp; // second & last send buffer
+ union {
+ LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirectParms;
+ LLC_DLC_OPEN_SAP_PARMS DlcOpenSapParms;
+ LLC_RECEIVE_PARMS ReceiveParms;
+ LLC_TRANSMIT_PARMS TransmitParms;
+ LLC_BUFFER_FREE_PARMS BufferFreeParms;
+ } Parms;
+ BOOL fIsFDDI = FALSE;
+ USHORT usFDDIStationId = 0;
+ INT source_offset = RPLDLC_DIRECT_SOURCE_OFFSET;
+
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "++EtherStartThread: pOpenInfo=0x%x", pOpenInfo));
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ BufferFree = FALSE;
+
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_FDDI ) {
+ fIsFDDI = TRUE;
+ source_offset = RPLDLC_SAP_SOURCE_OFFSET;
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "EtherStartThread: fIsFDDI==TRUE"));
+ }
+ //
+
+ //
+ // Initialize fixed fields in ccb.
+ //
+ memset( &ccb, '\0', sizeof(ccb));
+ ccb.hCompletionEvent = CreateEvent(
+ NULL, // no security attributes
+ FALSE, // automatic reset
+ FALSE, // initial state is NOT signalled
+ NULL // no name
+ );
+ if ( ccb.hCompletionEvent == NULL) {
+ DWORD status = GetLastError();
+ RplDump( ++RG_Assert, ( "error=%d", status));
+ RplDlcReportEvent( status, SEM_CREATE);
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ return;
+ }
+ ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ ccb.u.pParameterTable = (PLLC_PARMS)&Parms;
+
+ //
+ // Initialize fixed fields in XmitQueue.
+ //
+ memset( (PVOID)&XmitQueue, '\0', sizeof( XmitQueue.XmitBuffer));
+ XmitQueue.XmitBuffer.cbBuffer = sizeof( XmitQueue.LanHeader);
+ RplCopy( XmitQueue.LanHeader.source_addr, pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+ //
+ // Routing info header fields should be important for token ring already.
+ // However DLC may want to use these fields to set the XNS identifier.
+ // For now, 0x0600 word is hardcoded in DLCAPI.DLL // BUGBUG
+ //
+ XmitQueue.LanHeader.routing_info_header[0] = 0x06;
+ XmitQueue.LanHeader.routing_info_header[1] = 0x00;
+
+ //
+ // Initialize fixed fields in SnapResp.data.response.
+ //
+ memset( &SnapResp.data.response, '\0', sizeof(SnapResp.data.response));
+ SnapResp.data.response.idp.chksum = XNS_NO_CHKSUM;
+ SnapResp.data.response.idp.packet_type = PEX_TYPE;
+ SnapResp.data.response.idp.dest.socket = ETHERSTART_SOCKET;
+ RplCopy( SnapResp.data.response.idp.src.host, pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+ SnapResp.data.response.idp.src.socket = ETHERSTART_SOCKET;
+ SnapResp.data.response.pex.pex_client = ETHERSERIES_CLIENT;
+ SnapResp.data.response.es.func = FUNC_READFILERESP;
+
+ memset( &Parms, '\0', sizeof(Parms));
+ if (fIsFDDI) {
+ //
+ // Prepare for DLC_OPEN_SAP.
+ //
+ // Parms.DlcOpenSapParms.usStationId=
+ // Parms.DlcOpenSapParms.usUserStatValue=
+ // Parms.DlcOpenSapParms.uchT1=
+ // Parms.DlcOpenSapParms.uchT2=
+ // Parms.DlcOpenSapParms.uchTi=
+ // Parms.DlcOpenSapParms.uchMaxOut=
+ // Parms.DlcOpenSapParms.uchMaxIn=
+ // Parms.DlcOpenSapParms.uchMaxOutIncr=
+ // Parms.DlcOpenSapParms.uchMaxRetryCnt=
+ // Parms.DlcOpenSapParms.uchMaxMembers=
+ // Parms.DlcOpenSapParms.usMaxI_Field=
+ Parms.DlcOpenSapParms.uchSapValue=SNAP_SAP;
+ Parms.DlcOpenSapParms.uchOptionsPriority=LLC_INDIVIDUAL_SAP;
+ Parms.DlcOpenSapParms.uchcStationCount=1;
+ // Parms.DlcOpenSapParms.uchReserved2[2]=
+ // Parms.DlcOpenSapParms.cGroupCount=
+ // Parms.DlcOpenSapParms.pGroupList=
+ // Parms.DlcOpenSapParms.DlcStatusFlags=
+ // Parms.DlcOpenSapParms.uchReserved3[8]=
+ // Parms.DlcOpenSapParms.cLinkStationsAvail=
+ } else {
+ //
+ // Prepare for DIR_OPEN_DIRECT.
+ //
+ Parms.DirOpenDirectParms.usOpenOptions = LLC_DIRECT_OPTIONS_ALL_MACS;
+ Parms.DirOpenDirectParms.usEthernetType = 0x0600; // XNS identifier
+ Parms.DirOpenDirectParms.ulProtocolTypeMask = 0xFFFFFFFF;
+ Parms.DirOpenDirectParms.ulProtocolTypeMatch = 0x7200FFFF;
+ Parms.DirOpenDirectParms.usProtocolTypeOffset = 14; // where FFFF0072 of client begins
+ }
+ if ( !EtherAcsLan( pAdapterInfo,
+ &ccb,
+ (BYTE)((fIsFDDI) ? LLC_DLC_OPEN_SAP
+ : LLC_DIR_OPEN_DIRECT ))) {
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ CloseHandle( ccb.hCompletionEvent);
+ return;
+ }
+ if (fIsFDDI) {
+ usFDDIStationId = Parms.DlcOpenSapParms.usStationId;
+ }
+
+ for (;;) {
+ extern BYTE ripl_rom[];
+ extern WORD sizeof_ripl_rom;
+ WORD Temp;
+ SNAP_FRAME * psnapreq;
+
+ if ( BufferFree == TRUE) {
+ BufferFree = FALSE;
+ memset( &Parms, '\0', sizeof( Parms));
+ Parms.BufferFreeParms.pFirstBuffer = (PLLC_XMIT_BUFFER)pRcvbuf;
+ if ( !EtherAcsLan( pAdapterInfo, &ccb, LLC_BUFFER_FREE)) {
+ break;
+ }
+ }
+
+ //
+ // Prepare for RECEIVE.
+ //
+ // ReceiveParms.usStationId==0 means receive MAC & non-MAC frames.
+ //
+ memset( &Parms, '\0', sizeof(Parms));
+ if (fIsFDDI) {
+ Parms.ReceiveParms.usStationId = usFDDIStationId;
+ }
+ if ( !EtherAcsLan( pAdapterInfo, &ccb, LLC_RECEIVE)) {
+ break;
+ }
+ BufferFree = TRUE;
+
+ pRcvbuf = (PRCVBUF)Parms.ReceiveParms.pFirstBuffer;
+ if ( fIsFDDI ) {
+ psnapreq = (SNAP_FRAME *)&pRcvbuf->u;
+ if ( psnapreq->header.etype[0] != 0x06
+ || psnapreq->header.etype[1] != 0x00 ) {
+ continue;
+ }
+ pEtherstartReq = &(psnapreq->data.request);
+ }
+ else
+ {
+ pEtherstartReq = (ETHERSTART_REQ *)&pRcvbuf->u;
+ }
+
+ //
+ // Make sure this request is for us.
+ //
+ if ( !RplCrack( pEtherstartReq)) {
+ continue; // this request is not for us, ignore it
+ }
+
+ //
+ // Prepare for TRANSMIT_DIR_FRAME.
+ //
+ memset( &Parms, '\0', sizeof(Parms));
+ Parms.TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+ Parms.TransmitParms.pXmitQueue1 = (LLC_XMIT_BUFFER *)&XmitQueue;
+ Parms.TransmitParms.pBuffer1 = (fIsFDDI)
+ ? (PVOID)&SnapResp
+ : (PVOID)&SnapResp.data.response;
+
+ //
+ // Initialize variable fields in XmitQueue.
+ //
+ // We don't want to reverse bits here.
+ // CODEWORK The +2 here is probably made necessary by a DLC bug.
+ //
+ RplCopy( ((PBYTE)XmitQueue.LanHeader.dest_addr)
+ + ((fIsFDDI) ? 2 : 0),
+ &pRcvbuf->b.auchLanHeader[ source_offset],
+ NODE_ADDRESS_LENGTH);
+
+ //
+ // Initialize variable fields in SnapResp.data.response.
+ // For FDDI we use all of SnapResp.
+ //
+ Temp = min( pEtherstartReq->count,
+ (WORD)(sizeof_ripl_rom - pEtherstartReq->start));
+ Parms.TransmitParms.cbBuffer1 = (WORD)( sizeof(SnapResp.data.response) -
+ sizeof( SnapResp.data.response.data) + Temp );
+ if (fIsFDDI) {
+ Parms.TransmitParms.cbBuffer1 += sizeof(SnapResp.header);
+ Parms.TransmitParms.usStationId = usFDDIStationId;
+ Parms.TransmitParms.uchRemoteSap=SNAP_SAP;
+ RplCopy( &SnapResp.header,
+ &psnapreq->header,
+ sizeof(SnapResp.header) );
+ }
+ SnapResp.data.response.idp.len = HILO( Parms.TransmitParms.cbBuffer1);
+ RplCopy( SnapResp.data.response.idp.dest.host,
+ &pRcvbuf->b.auchLanHeader[ source_offset],
+ NODE_ADDRESS_LENGTH);
+ if (fIsFDDI) {
+ ReverseBits( SnapResp.data.response.idp.dest.host );
+ }
+ SnapResp.data.response.pex.pex_id = pEtherstartReq->pex.pex_id;
+ SnapResp.data.response.es.bytes = Temp; // stays intel ordered
+ // CODEWORK why do we need to copy every time?
+ RplCopy( SnapResp.data.response.data, &ripl_rom[ pEtherstartReq->start], Temp);
+
+ if ( !EtherAcsLan( pAdapterInfo, &ccb,
+ (BYTE)((fIsFDDI) ? LLC_TRANSMIT_UI_FRAME
+ : LLC_TRANSMIT_DIR_FRAME ))) {
+ break;
+ }
+ }
+
+ if ( pAdapterInfo->Closing == FALSE) {
+ RplDump(++RG_Assert, (
+ "pInfo=0x%x &ccb=0x%x pReq=0x%x pRcvbuf=0x%x pXmitQueue=0x%x pResp=0x%x &Parms=0x%x",
+ pAdapterInfo, &ccb, pEtherstartReq, pRcvbuf, &XmitQueue,
+ (fIsFDDI) ? (PVOID)&SnapResp : (PVOID)&SnapResp.data.response, &Parms));
+ RplReportEvent( NELOG_RplXnsBoot, NULL, 0, NULL);
+ }
+ if ( BufferFree == TRUE) {
+ memset( &Parms, '\0', sizeof( Parms));
+ Parms.BufferFreeParms.pFirstBuffer = (PLLC_XMIT_BUFFER)pRcvbuf;
+ (VOID)EtherAcsLan( pAdapterInfo, &ccb, LLC_BUFFER_FREE);
+ }
+ CloseHandle( ccb.hCompletionEvent);
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ETHER,(
+ "--EtherStartThread: pOpenInfo=0x%x", pOpenInfo));
+}
diff --git a/private/net/svcdlls/rpl/dll/fdr.c b/private/net/svcdlls/rpl/dll/fdr.c
new file mode 100644
index 000000000..dc7f53078
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/fdr.c
@@ -0,0 +1,181 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ fdr.c
+
+Abstract:
+
+ Contains RpldFileDataResponse() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+BOOL RplDlcFdr(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb,
+ PBYTE data_ptr,
+ WORD length,
+ DWORD locate_addr,
+ DWORD start_addr
+ )
+/*++
+
+Routine Description:
+ This function sends FILE.DATA.RESPONSE frames to the workstation.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+ data_ptr pointer to the data to be send
+ length length of the data
+ xfer_addr addr to place data on workstation
+ start_addr addr to start execution at
+
+Return Value:
+ ERROR_SUCCESS if success, else failure.
+
+--*/
+{
+ DWORD status;
+ PFDR pFrame;
+ PLLC_CCB pBadCcb;
+ PLAN_RESOURCE pLanResource;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ pFrame = (PFDR)pLanResource->frame;
+
+ pLanResource->xmit_error_cnt = 0; // Zero consecutive xmit error counter
+ pLanResource->retry_count = 0; // allow retries
+
+transmit_retry:
+ (void)memset( (LPVOID)&pLanResource->ccb, '\0', sizeof( pLanResource->ccb));
+
+ pLanResource->TransmitParms.cbBuffer2 = length;
+ pLanResource->TransmitParms.pBuffer2 = data_ptr;
+
+ // len = 0x1d + len data bit
+ pFrame->program_length = HILO(length + (sizeof (*pFrame)));
+
+ pFrame->data_hdr = HILO(length + 4); // set len part of data hdr
+// #define RPL_ELNK
+#ifdef RPL_ELNK
+ if ( length < 0x544) {
+ pFrame->data_hdr |= 0xFFFF0000;
+ }
+#endif
+ pFrame->seq_num = HILO2(pRcb->fdr_seq_number);
+ pFrame->flags = *((PBYTE)&pRcb->send_flags);
+ pFrame->locate_addr = HILO2(locate_addr);
+ pFrame->xfer_addr = HILO2(start_addr);
+
+
+ if ( !ResetEvent( pRcb->txsemhdl)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ pLanResource->ccb.hCompletionEvent = pRcb->txsemhdl;
+ pLanResource->ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pLanResource->ccb.uchDlcCommand = LLC_TRANSMIT_UI_FRAME;
+ pLanResource->ccb.u.pParameterTable = (PLLC_PARMS)&pLanResource->TransmitParms;
+
+ if ( !ResetEvent( pRcb->SF_wakeup)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ pRcb->RcbIsBad = TRUE;
+ return( FALSE);
+ }
+
+ status = AcsLan( &pLanResource->ccb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pCcb=0x%x pBadCcb=0x%x status=%d",
+ &pLanResource->ccb, pBadCcb, status));
+ RplDlcReportEvent( status, LLC_TRANSMIT_UI_FRAME);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pRcb->txsemhdl, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ((status != WAIT_OBJECT_0) || (pLanResource->ccb.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( pLanResource->retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "FDR(%ws): retry_count=%d, status=%d, DlcStatus=0x%x",
+ pRcb->AdapterName, pLanResource->retry_count, status,
+ pLanResource->ccb.uchDlcStatus));
+ Sleep( 100L * pLanResource->retry_count); // for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,(
+ "pCcb=0x%x pBadCcb=0x%x AdapterName=%ws status=%d DlcStatus=0x%x",
+ &pLanResource->ccb, pBadCcb, pRcb->AdapterName, status, pLanResource->ccb.uchDlcStatus));
+
+ pLanResource->retry_count = 0; // no more attempts, reset retry count
+
+ if ( status != ERROR_SUCCESS) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ (void)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+
+ }
+
+ if ( pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR_FS
+ ||
+ pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR) {
+ //
+ // Workstation is not receiving anymore. ?? Rebooted
+ // during boot, power failure or etc. OR real transmit
+ // failure ??
+
+ if ( pLanResource->xmit_error_cnt++ <= MAX_CONSECUTIVE_ERROR) {
+ return( FALSE);
+ }
+
+ //
+ // Too many consecutive transmit errors. Tell that there is a
+ // problem with transmitting.
+ //
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/dll/find.c b/private/net/svcdlls/rpl/dll/find.c
new file mode 100644
index 000000000..0527aa668
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/find.c
@@ -0,0 +1,312 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ find.c
+
+Abstract:
+
+ Contains RplDlcFind() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+Notes:
+
+ June 21, 1990 Olli Visamo
+ If RplDlcFind() gets error LLC_STATUS_LOST_DATA_NO_BUFFERS or
+ LLC_STATUS_LOST_DATA_INADEQUATE_SPACE from LLC_RECEIVE, go back to
+ receive loop since these errors are recoverable.
+
+ July 30, 1990 Olli Visamo
+ If there is VolumeId in FIND frame, it is copied to RCB structure.
+--*/
+
+#include "local.h"
+
+DBGSTATIC VOID BinaryToUnicode(
+ OUT LPWSTR UnicodeString,
+ IN PBYTE BinaryArray,
+ IN DWORD BinaryArrayLength
+ )
+{
+ BYTE byte;
+ DWORD index;
+
+ for ( index=0; index < BinaryArrayLength; index++) {
+ byte = BinaryArray[ index];
+ UnicodeString[ 2*index] = L"0123456789ABCDEF"[ byte / 0x10];
+ UnicodeString[ 2*index+1] = L"0123456789ABCDEF"[ byte & 0x0F];
+ }
+ UnicodeString[ 2*index] = 0;
+}
+
+
+BOOL RplDlcFind(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ )
+/*++
+Routine Description:
+
+ Activates DLC receive to pick up FIND frames. FIND frames arrive on
+ FIND_SAP sid.
+
+ We do not log any events if we encounter errors while service is in
+ the closing state. But we have to return FALSE even in that case
+ else the caller would keep calling us in a tight loop.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+
+Return Value:
+ TRUE if a valid FIND request was received
+ FALSE otherwise
+
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PRCVBUF pRcvbuf;
+ PADAPTER_INFO pAdapterInfo;
+ PLAN_RESOURCE pLanResource;
+ PFOUND_FRAME pFoundFrame;
+ WORD frame_size;
+ WORD volid_len;
+ WORD prog_len;
+ WORD i;
+
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ memset( (PVOID)&pAdapterInfo->u1, '\0', sizeof(pAdapterInfo->u1));
+ pAdapterInfo->u1.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u1.r.uchDlcCommand = LLC_RECEIVE;
+ pAdapterInfo->u1.r.hCompletionEvent = pAdapterInfo->findsem;
+ pAdapterInfo->u1.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u1.r1;
+
+ pAdapterInfo->u1.r1.usStationId = pAdapterInfo->findsid;
+
+rcvloop:
+ if ( !ResetEvent( pAdapterInfo->findsem)) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( &pAdapterInfo->u1.r, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, LLC_RECEIVE);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pAdapterInfo->findsem, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pAdapterInfo->u1.r.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else if ( pAdapterInfo->u1.r.uchDlcStatus
+ == LLC_STATUS_LOST_DATA_NO_BUFFERS) {
+ goto rcvloop; // recoverable error
+ } else if ( pAdapterInfo->u1.r.uchDlcStatus
+ == LLC_STATUS_LOST_DATA_INADEQUATE_SPACE) {
+ //
+ // Release the buffer first. We do not expect an error here.
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ goto rcvloop;
+ } else {
+ RplDlcReportEvent( pAdapterInfo->u1.r.uchDlcStatus, LLC_RECEIVE);
+ }
+ RplDump( ++RG_Assert,( "status=%d, DlcStatus=0x%x",
+ status, pAdapterInfo->u1.r.uchDlcStatus));
+ return( FALSE);
+ }
+
+ pRcvbuf = (PRCVBUF) (pAdapterInfo->u1.r1.pFirstBuffer);
+
+ if ( pRcvbuf->u.Find.program_command != FIND_CMD) {
+ //
+ // NOT a FIND frame, ignore it. And of course, release the buffer.
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ goto rcvloop; // Issue new receive
+ }
+
+ //
+ // Save burned in address first in binary then in hex-ascii representation.
+ //
+ memcpy( pRcb->NodeAddress, pRcvbuf->u.Find.source_addr, NODE_ADDRESS_LENGTH);
+ BinaryToUnicode( pRcb->AdapterName, pRcb->NodeAddress, NODE_ADDRESS_LENGTH);
+
+ // Volume id option and default boot bit are not necessarily used
+ pRcb->volume_id[0] ='\0';
+ pRcb->flags.defboot = 0;
+
+ // Check if volumeid is passed (Nokia option)
+ prog_len = HILO(pRcvbuf->u.Find.program_length);
+ if (prog_len > FIND_LEN) {
+ //
+ // To get volume id length, we subtract 6 (4 for FILE_HDR length
+ // plus 2 for identifier bytes)
+ //
+ volid_len = (WORD)((HIBYTE((LOWORD(pRcvbuf->u.Find.file_hdr)))) - 6);
+
+ if (volid_len <= MAX_VOLID_LEN && volid_len >= 0) {
+ if (pRcvbuf->u.Find.file_name[0] == VOLID_FIELD1 &&
+ pRcvbuf->u.Find.file_name[1] == VOLID_FIELD2) {
+ // IT IS VOLUME ID
+ for (i = 0; i < volid_len; i++) {
+ pRcb->volume_id[i] = pRcvbuf->u.Find.file_name[i+2];
+ }
+ }
+ }
+ }
+
+ //
+ // PREPARE LAN HEADER AND TRANSMIT BUFFERS IN LAN RCB FOR
+ // FOUND FRAME TRANSMISSION
+ //
+
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ memcpy( (PVOID)pLanResource->LanHeader.dest_addr,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ 8], NODE_ADDRESS_LENGTH);
+
+ //
+ // Set routing stuff for TokenRing only.
+ //
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING) {
+ //
+ // Clear msb in first byte to avoid GROUP address
+ //
+ pLanResource->LanHeader.dest_addr[0] &= 0x7F;
+ //
+ // Send it using limited broadcast.
+ //
+ pLanResource->LanHeader.routing_info_header[0] = BRDCST;
+ pLanResource->LanHeader.routing_info_header[1] = UNSPEC_FRM_SIZE;
+ //
+ // Set routing info indication bit
+ //
+ pLanResource->LanHeader.source_addr[0] |= 0x80;
+ }
+
+ //
+ // Setup LLC_TRANSMIT_PARMS (transmit parameter table).
+ //
+ pLanResource->TransmitParms.usStationId = pAdapterInfo->findsid; // our, send Sid
+ pLanResource->TransmitParms.uchTransmitFs = 0;
+ pLanResource->TransmitParms.uchRemoteSap = FIND_SAP; // their, receive SAP
+ pLanResource->TransmitParms.pXmitQueue1 =
+ (PLLC_XMIT_BUFFER)&pLanResource->XmitBuffer;
+ pLanResource->TransmitParms.pXmitQueue2 = NULL;
+ //
+ // Buffer 1 is FOUND_FRAME. There is no buffer 2.
+ //
+ pLanResource->TransmitParms.cbBuffer1 = sizeof( FOUND_FRAME);
+ pLanResource->TransmitParms.pBuffer1 = (PVOID)pLanResource->frame;
+ pLanResource->TransmitParms.cbBuffer2 = 0;
+ pLanResource->TransmitParms.pBuffer2 = NULL;
+ //
+ // Do not chain on completion.
+ //
+ pLanResource->TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+
+
+ //
+ // Setup LLC_XMIT_BUFFER used for "pXmitQueue1" above. Note that in
+ // LAN_RESOURCE data structure LanHeader field is right behind
+ // XmitBuffer field. Alignment should cause no problems (for now)
+ // since TransmitParms size is 0xc.
+ //
+ pLanResource->XmitBuffer.pNext = NULL;
+ pLanResource->XmitBuffer.cbBuffer = sizeof( pLanResource->LanHeader);
+ pLanResource->XmitBuffer.cbUserData = 0;
+
+ // Prepare FOUND frame, there is space for it in LAN RCB
+
+ //
+ // Choose frame_size as smaller of our frame size & client's
+ // frame size.
+ //
+
+ frame_size = HILO(pRcvbuf->u.Find.max_frame); // in Intel format
+
+ if ( frame_size > pAdapterInfo->max_xmit_buff_size) {
+ //
+ // Our frame size is smaller than client's frame size.
+ //
+ frame_size = pAdapterInfo->max_xmit_buff_size;
+ }
+
+ pFoundFrame = (PFOUND_FRAME)pLanResource->frame; // frame area of rcb
+ pFoundFrame->max_frame = HILO( frame_size); // in LAN format
+ pFoundFrame->program_length = FL; // 00 3A
+ pFoundFrame->program_command = FOUND_CMD; // 00 02
+ pFoundFrame->corr_header = CORR_HDR; // 00 08 40 03
+ pFoundFrame->correlator = 0; // 00 00 00 00
+ pFoundFrame->resp_hdr = RESP_HDR; // 00 05 40 0B
+ pFoundFrame->resp_code = 0; // 00
+ pFoundFrame->dest_hdr = DEST_HDR; // 00 0a 40 0c
+ pFoundFrame->source_hdr = SOURCE_HDR; // 00 0a 40 06
+ pFoundFrame->info_hdr = INFO_HDR; // 00 10 00 08
+ pFoundFrame->frame_hdr = FRAME_HDR; // 00 06 40 09
+ pFoundFrame->class_hdr = CLASS_HDR; // 00 06 40 0a
+ pFoundFrame->conn_class = CLASS1; // 00 01
+ pFoundFrame->lsap_hdr = LSAP_HDR; // 00 05 40 07
+ pFoundFrame->rsap = LOAD_SAP; // F8
+
+ //
+ // Put our card-ID in frame we're transmitting. Remote boot client
+ // will use this address as a destination address for sending its
+ // SEND.FILE.REQUEST frames.
+ //
+ memcpy( (PVOID)pFoundFrame->source_addr,
+ pOpenInfo->NodeAddress, NODE_ADDRESS_LENGTH);
+
+ //
+ // put wrksta-id in frame they're RECEIVING
+ //
+ memcpy( (PVOID)pFoundFrame->dest_addr,
+ pRcvbuf->u.Find.source_addr, NODE_ADDRESS_LENGTH);
+
+ //
+ // release the buffer
+ //
+ if ( !RplDlcBufferFree( FIND_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/dll/found.c b/private/net/svcdlls/rpl/dll/found.c
new file mode 100644
index 000000000..d9e4469a8
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/found.c
@@ -0,0 +1,159 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ found.c
+
+Abstract:
+
+ Contains RpldFound() entry point.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+
+BOOL RplDlcFound(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ This function sends FOUND frame to the workstation.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ pRcb pointer to the RESOURCE CONTROL BLOCK structure
+
+Return Value:
+ ERROR_SUCCESS if success, else failure.
+
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PLAN_RESOURCE pLanResource;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+
+ pLanResource->xmit_error_cnt = 0; // Zero consecutive xmit error counter
+ pLanResource->retry_count = 0; // allow retries
+
+transmit_retry:
+ (void)memset( (LPVOID)&pLanResource->ccb, '\0', sizeof( pLanResource->ccb));
+ //
+ // Most of other FOUND_FRAME data have been already set in RpldFind() code.
+ //
+ pLanResource->TransmitParms.cbBuffer2 = 0;
+ pLanResource->TransmitParms.pBuffer2 = NULL;
+
+ if ( !ResetEvent( pRcb->txsemhdl)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ pLanResource->ccb.hCompletionEvent = pRcb->txsemhdl;
+ pLanResource->ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pLanResource->ccb.uchDlcCommand = LLC_TRANSMIT_UI_FRAME;
+ pLanResource->ccb.u.pParameterTable = (PLLC_PARMS)&pLanResource->TransmitParms;
+
+ if ( !ResetEvent( pRcb->SF_wakeup)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ pRcb->RcbIsBad = TRUE;
+ return( FALSE);
+ }
+
+ status = AcsLan( &pLanResource->ccb, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( "pCcb=0x%x pBadCcb=0x%x status=%d",
+ &pLanResource->ccb, pBadCcb, status));
+ RplDlcReportEvent( status, LLC_TRANSMIT_UI_FRAME);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pRcb->txsemhdl, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( (status != WAIT_OBJECT_0) || (pLanResource->ccb.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ }
+ if ( pLanResource->retry_count++ < MAXRETRY) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_FOUND,(
+ "FDR(%ws): retry_count=%d, status=%d, DlcStatus=0x%x",
+ pRcb->AdapterName, pLanResource->retry_count, status,
+ pLanResource->ccb.uchDlcStatus));
+ Sleep( 100L * pLanResource->retry_count); // for NETWORK to recover
+ goto transmit_retry;
+ }
+ RplDump( ++RG_Assert,(
+ "pCcb=0x%x pBadCcb=0x%x AdapterName=%ws status=%d DlcStatus=0x%x",
+ &pLanResource->ccb, pBadCcb, pRcb->AdapterName, status, pLanResource->ccb.uchDlcStatus));
+
+ pLanResource->retry_count = 0; // no more attempts, reset retry count
+
+ if ( status != ERROR_SUCCESS) {
+ RplDlcReportEvent( status, SEM_WAIT);
+ (VOID)SetEvent( pRcb->txsemhdl);
+ return( FALSE);
+
+ }
+
+ if ( pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR_FS
+ ||
+ pLanResource->ccb.uchDlcStatus == LLC_STATUS_TRANSMIT_ERROR) {
+ //
+ // Workstation is not receiving anymore. ?? Rebooted
+ // during boot, power failure or etc. OR real transmit
+ // failure ??
+
+ if ( pLanResource->xmit_error_cnt++ <= MAX_CONSECUTIVE_ERROR) {
+ return( FALSE);
+ }
+
+ //
+ // Too many consecutive transmit errors. Tell that there is a
+ // problem with transmitting.
+ //
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ RplDlcReportEvent( pLanResource->ccb.uchDlcStatus, LLC_TRANSMIT_UI_FRAME);
+ return( FALSE);
+ }
+
+ return( TRUE);
+}
+
diff --git a/private/net/svcdlls/rpl/dll/init.c b/private/net/svcdlls/rpl/dll/init.c
new file mode 100644
index 000000000..0550b158f
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/init.c
@@ -0,0 +1,545 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ Contains RplDlcInit() and RplDlcTerm() entry points.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+//
+// Routines imported from ether.c
+//
+VOID EtherStartThread( POPEN_INFO pOpenInfo);
+
+// Some definitions for EtherStart
+#define ETHERSTART_STACK_SIZE 0x1400 // 5k
+
+BYTE Swap[256] =
+{
+0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
+0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
+0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
+0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
+0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
+0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
+0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
+0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
+0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
+0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
+0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
+0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
+0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
+};
+
+VOID ReverseBits( PBYTE NodeAddress) {
+ DWORD i;
+ for ( i = 0; i < NODE_ADDRESS_LENGTH; i++) {
+ NodeAddress[ i] = Swap[ NodeAddress[ i] ];
+ }
+}
+
+DBGSTATIC BOOL AdapterInit(
+ POPEN_INFO pOpenInfo,
+ BYTE command,
+ BYTE sapval
+ )
+/*++
+Routine Description:
+
+ Contains functions used to prepare network adapter to serve remote boot.
+ Following DLC calls (ACSLAN) are used
+
+ - DIR.OPEN.ADAPTER
+ - DIR.STATUS
+ - DLC.OPEN.SAP
+ - DIR.SET.FUNCTIONAL.ADDRESS
+
+ Setup parameters for command (CCB and parameter tables).
+ We do not use an application-ID to lock our DLC resources.
+ Call ACSLAN.
+ Check immediate return code, quit on error, no retry.
+ Wait on the semaphore for command completion. check status.
+ Parse result of command when necessary.
+ - OPEN_ADAPTER
+ Record the Appl-ID value for future DLC calls.
+ Get max. transmit buffer size.
+ - STATUS
+ Place adapter-ID in the openinfo structure.
+ Place adapter-type in the openinfo structure.
+ - OPEN_SAP
+ Place station ids in the adapter_info structure
+ - SET_FUNCTIONAL_ADDRESS (TokenRing) or
+ SET_GROUP_ADDRESS (Ethernet)
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb; // unused pointer for ACSLAN
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ if ( !ResetEvent( pAdapterInfo->gen_sem)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ //
+ // An early check - compare dwords - do not subtract - to avoid wrapping.
+ //
+ if ( pOpenInfo->adapt_info_size <= sizeof( ADAPTER_INFO)) {
+ RplDump( ++RG_Assert,( "adapt_info_size=%d", pOpenInfo->adapt_info_size));
+ return( FALSE); // no space for DLC buffer pool
+ }
+
+ //
+ // Zero parameter blocks (this will zero "pNext" field in LLC_CCB)
+ // and do the common parameter initializion.
+ //
+ (VOID)memset((PVOID)&pAdapterInfo->s,'\0',sizeof(pAdapterInfo->s));
+ pAdapterInfo->s.ccb.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->s.ccb.uchDlcCommand = command;
+ pAdapterInfo->s.ccb.hCompletionEvent = pAdapterInfo->gen_sem;
+
+ //
+ // Initialize command specific parameters in the input LLC_CCB.
+ //
+ switch( command) {
+ case LLC_BUFFER_CREATE:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s0.bcp;
+ pAdapterInfo->s.u.s0.bcp.pBuffer =
+ pOpenInfo->adapt_info_ptr + sizeof( ADAPTER_INFO);
+ pAdapterInfo->s.u.s0.bcp.cbBufferSize =
+ pOpenInfo->adapt_info_size - sizeof( ADAPTER_INFO);
+ pAdapterInfo->s.u.s0.bcp.cbMinimumSizeThreshold = 0x2000;
+ break;
+
+ case LLC_DIR_CLOSE_ADAPTER:
+ NOTHING; // there is nothing to initialize here
+ break;
+ case LLC_DIR_OPEN_ADAPTER:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s1.cpo;
+ pAdapterInfo->s.u.s1.cpo.pAdapterParms = &pAdapterInfo->s.u.s1.oap;
+ pAdapterInfo->s.u.s1.cpo.pExtendedParms = &pAdapterInfo->s.u.s1.oep;
+ pAdapterInfo->s.u.s1.oep.pSecurityDescriptor = NULL;
+ //
+ // With LLC_ETHERNET_TYPE_DEFAULT rpl server on build 392 did not
+ // detect an 802.3 client FIND frame.
+ //
+#ifdef NOT_YET
+ // Antti's email
+ pAdapterInfo->s.u.s1.oep.LlcEthernetType = LLC_ETHERNET_TYPE_DIX;
+#else
+ // Old stuff
+ pAdapterInfo->s.u.s1.oep.LlcEthernetType = LLC_ETHERNET_TYPE_802_3;
+#endif
+ pAdapterInfo->s.u.s1.oep.hBufferPool = NULL; // for first open
+ pAdapterInfo->s.u.s1.cpo.pDlcParms = &pAdapterInfo->s.u.s1.odp;
+ break;
+
+ case LLC_DIR_STATUS:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s2.dsp;
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+ pAdapterInfo->s.ccb.u.pParameterTable =
+ (PLLC_PARMS)&pAdapterInfo->s.u.s3.cpo;
+ pAdapterInfo->s.u.s3.cpo.uchOptionsPriority = (BYTE ) 0x04; // individual sap
+ pAdapterInfo->s.u.s3.cpo.uchSapValue = sapval; // the SAP number to open
+ break;
+
+ case LLC_DIR_SET_FUNCTIONAL_ADDRESS: // TokenRing
+ //
+ // This works correctly for Ethernet medium as well. I.e. in Ethernet
+ // case bits within every byte are inverted by DLC. We keep the code
+ // for SET_GROUP_ADDRESS in case DLC gets changed not to support this.
+ //
+ pAdapterInfo->s.ccb.u.ulParameter = 0x00000040; // accept FIND frames
+ break;
+
+ case LLC_DIR_SET_GROUP_ADDRESS: // Ethernet
+ //
+ // Use TokenRing FUNCTIONAL_ADDRESS given above, but with bits
+ // inverted in every single byte (order of bytes is unchanged).
+ //
+ pAdapterInfo->s.ccb.u.ulParameter = 0x00000002; // accept FIND frames
+ break;
+ default:
+ RplDump( ++RG_Assert, ("command=0x%x", command));
+ return( FALSE);
+ break;
+ }
+
+ status = AcsLan( &pAdapterInfo->s.ccb, &pBadCcb);
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "Adapter: AcsLan(0x%x), status=%d", command, status));
+
+ if ( status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+
+ //
+ // We do not write an event log if we fail to open an adapter.
+ // We assume we may be trying to open an adapter that does not even exist.
+ //
+ if ( command == LLC_DIR_OPEN_ADAPTER) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: AcsLan( DIR_OPEN_ADAPTER) fails, status=0x%x, adapterNumber=%d",
+ status, pAdapterInfo->adapter_number));
+ } else {
+ // Look up ACSLAN_STATUS errors.
+ RplDump( ++RG_Assert,( "AcsLan: command=0x%x, status=0x%x, adapterNumber=%d",
+ command, status, pAdapterInfo->adapter_number));
+ RplDlcReportEvent( status, command);
+ }
+ (VOID)SetEvent( pAdapterInfo->gen_sem);
+ return( FALSE);
+ }
+
+ //
+ // await command completion for few seconds
+ //
+ status = WaitForSingleObject( pAdapterInfo->gen_sem, 3000L);
+
+ if (status != WAIT_OBJECT_0) {
+ if ( status == -1) {
+ status = GetLastError();
+ }
+ RplDump( ++RG_Assert,( "Adapter: Wait(), status=%d", status));
+ RplDlcReportEvent( status, SEM_WAIT);
+ (VOID)SetEvent( pAdapterInfo->gen_sem);
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: AcsLan( command=0x%x), DlcStatus=0x%x",
+ command, pAdapterInfo->s.ccb.uchDlcStatus));
+
+ if ( pAdapterInfo->s.ccb.uchDlcStatus) {
+ RplDump( ++RG_Assert,( "Adapter: DlcStatus=0x%x", pAdapterInfo->s.ccb.uchDlcStatus));
+ RplDlcReportEvent( pAdapterInfo->s.ccb.uchDlcStatus, command);
+ return( FALSE);
+ }
+
+ switch( command) {
+ case LLC_BUFFER_CREATE:
+ pAdapterInfo->hBufferPool = pAdapterInfo->s.u.s0.bcp.hBufferPool;
+ RPL_ASSERT( pAdapterInfo->hBufferPool != NULL);
+ break;
+
+ case LLC_DIR_OPEN_ADAPTER:
+ //
+ // Save our maximum frame size for transmit buffers.
+ //
+ pAdapterInfo->max_xmit_buff_size =
+ pAdapterInfo->s.u.s1.oap.usMaxFrameSize;
+
+ //
+ // OS/2 dlc uses smaller size, thus if uncomment the definition
+ // of OS2_MAX_FRAME_SIZE below we can repro OS/2 RPL behavior
+ // byte by byte. Useful for debugging.
+ //
+#define OS2_MAX_FRAME_SIZE 1508
+
+#ifdef OS2_MAX_FRAME_SIZE
+ if (pAdapterInfo->max_xmit_buff_size > OS2_MAX_FRAME_SIZE) {
+ pAdapterInfo->max_xmit_buff_size = OS2_MAX_FRAME_SIZE;
+ }
+#endif
+
+ if ( pAdapterInfo->max_xmit_buff_size < OVRHD + 100) {
+ RplDump( ++RG_Assert,( "Adapter: max_xmit_buff_size=0x%x",
+ pAdapterInfo->max_xmit_buff_size));
+ RplDlcReportEvent( TOO_SMALL_XMIT_BUFF, RPLDLL_NO_ERROR);
+ return( FALSE);
+ }
+
+ // Tell the size of LAN_RCB, server allocates space for it.
+ pOpenInfo->lan_rcb_size = sizeof(LAN_RESOURCE);
+ break;
+
+ case LLC_DIR_STATUS:
+ pAdapterInfo->network_type = pAdapterInfo->s.u.s2.dsp.usAdapterType;
+ if ( pAdapterInfo->network_type != RPL_ADAPTER_ETHERNET &&
+ pAdapterInfo->network_type != RPL_ADAPTER_FDDI &&
+ pAdapterInfo->network_type != RPL_ADAPTER_TOKEN_RING) {
+ RplDump( ++RG_Assert,( "Adapter: network_type=0x%x",
+ pAdapterInfo->network_type));
+ return( FALSE); // unknown adapter type
+ }
+
+ memcpy( pOpenInfo->NodeAddress,
+ pAdapterInfo->s.u.s2.dsp.auchNodeAddress, NODE_ADDRESS_LENGTH);
+#ifndef NOT_YET
+ //
+ // In Ethernet (FDDI) case "auchNodeAddress" returned by DLC has
+ // wrong order of bits within a byte. Until DLC gets fixed
+ // we need to reverse the bit order. Without this the source
+ // address in FOUND frame is wrong, thus remote boot client
+ // uses wrong address in LAN_HEADER of its SEND_FILE_REQUEST.
+ //
+
+ if ( pAdapterInfo->network_type != RPL_ADAPTER_TOKEN_RING) {
+ ReverseBits( pOpenInfo->NodeAddress);
+ }
+#endif
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+ //
+ // Save station ids for future use
+ //
+ if (sapval == LOAD_SAP) {
+ pAdapterInfo->loadsid = pAdapterInfo->s.u.s3.cpo.usStationId;
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: loadsid = 0x%x", pAdapterInfo->loadsid));
+ } else {
+ pAdapterInfo->findsid = pAdapterInfo->s.u.s3.cpo.usStationId;
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,(
+ "Adapter: findsid = 0x%x", pAdapterInfo->findsid));
+ }
+ break;
+
+ case LLC_DIR_CLOSE_ADAPTER:
+ //
+ // Close events that are no longer needed.
+ //
+ (VOID)CloseHandle( pAdapterInfo->gen_sem);
+ (VOID)CloseHandle( pAdapterInfo->sfrsem);
+ (VOID)CloseHandle( pAdapterInfo->findsem);
+ (VOID)CloseHandle( pAdapterInfo->getdata_sem);
+ break;
+ }
+
+ return( TRUE);
+}
+
+BOOL RplDlcInit(
+ POPEN_INFO pOpenInfo,
+ DWORD rpl_adapter
+ )
+/*++
+
+Routine Description:
+
+ Prepares DLL and adapter to serve Remote Boot clients.
+
+ Create system events, open adapter logically and get adapter status,
+ open architected SAPs (F8 and FC), then set architected functional address.
+ This last operation succeeds in case of Tokenring. It fails in case of
+ Ethernet, so there we set group address.
+
+ In case of errors we write an event log and return.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ rpl_adapter adapter number
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ DWORD status;
+ PADAPTER_INFO pAdapterInfo;
+ DWORD index;
+ HANDLE Handle;
+
+ //
+ // Memory for pAdapterInfo was allocated in rpl server code.
+ //
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ //
+ // Initialize ADAPTER_INFO structure.
+ //
+
+ //
+ // Service is just starting => Closing is FALSE.
+ //
+ pAdapterInfo->Closing = FALSE;
+
+ //
+ // Typecast below reflects mismatch between RplDlc* & DLC interface.
+ //
+ pAdapterInfo->adapter_number = (BYTE)rpl_adapter;
+
+ //
+ // SFR receive thread is not yet created, init status variables
+ //
+ pAdapterInfo->SfrReceiveThreadHandle = NULL;
+ pAdapterInfo->SfrReturn = FALSE;
+
+ for ( status = ERROR_SUCCESS, index=0; index<4; index++) {
+ BOOL ManualReset;
+ BOOL InitialStateSignalled;
+ //
+ // There are four events to create and save handles to proper places
+ // for future use. Note that "sfrsem" is different.
+ //
+ if ( index != 1) {
+ ManualReset = TRUE;
+ InitialStateSignalled = TRUE;
+ } else {
+ ManualReset = FALSE;
+ InitialStateSignalled = FALSE;
+ }
+ Handle = CreateEvent( NULL, ManualReset, InitialStateSignalled, NULL);
+ if ( Handle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ break;
+ }
+ switch( index) {
+ case 0:
+ pAdapterInfo->gen_sem = Handle;
+ break;
+ case 1:
+ pAdapterInfo->sfrsem = Handle;
+ break;
+ case 2:
+ pAdapterInfo->findsem = Handle;
+ break;
+ case 3:
+ pAdapterInfo->getdata_sem = Handle;
+ break;
+ }
+ }
+
+ if ( status != ERROR_SUCCESS) { // failed to create events
+ RplDlcReportEvent( status, SEM_CREATE);
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_OPEN_ADAPTER, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_BUFFER_CREATE, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_STATUS, 0)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DLC_OPEN_SAP, LOAD_SAP)) {
+ return( FALSE);
+ }
+ if ( !AdapterInit( pOpenInfo, LLC_DLC_OPEN_SAP, FIND_SAP)) {
+ return( FALSE);
+ }
+
+ //
+ // For now LLC_DIR_SET_FUNCTIONAL_ADDRESS does the correct thing both
+ // in case of Ethernet & TokenRing. If due to unforeseen changes in
+ // DLC this code stops working on Ethernet case, we should call
+ // LLC_DIR_SET_GROUP_ADDRESS in Ethernet case below.
+ //
+ if ( !AdapterInit(
+ pOpenInfo,
+#ifdef NOT_YET
+ pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING
+ ? LLC_DIR_SET_FUNCTIONAL_ADDRESS
+ || LLC_DIR_SET_GROUP_ADDRESS,
+#else
+ LLC_DIR_SET_FUNCTIONAL_ADDRESS,
+#endif
+ 0)) {
+
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->network_type == RPL_ADAPTER_ETHERNET
+ || pAdapterInfo->network_type == RPL_ADAPTER_FDDI ) {
+
+ DWORD tid;
+
+ pAdapterInfo->EtherStartThreadHandle = CreateThread(
+ NULL, // lpThreadAttribute
+ 0, // dwStackSize - same as first thread
+ (LPTHREAD_START_ROUTINE)EtherStartThread, // lpStartAddress
+ (LPVOID)pOpenInfo, // lpParameter
+ 0, // dwCreationFlags
+ &tid // lpThreadId
+ );
+ if ( pAdapterInfo->EtherStartThreadHandle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "CreateThread(), status=%d", status));
+ RplDlcReportEvent( status, THREAD_CREATE);
+ return( FALSE);
+ }
+ }
+
+ return( TRUE);
+}
+
+BOOL RplDlcTerm( POPEN_INFO pOpenInfo)
+/*++
+
+Routine Description:
+ Releases adapter and other resources that have been allocated for RPLDLL.
+
+ This thread must wait for internally created RPLDLL threads to die.
+ RPLSRV does not know about this threads. If we do not wait here, then
+ RPLSRV may clean up all resources (e.g. debug buffer) before RPLDLL
+ internal threads manage to die (=>access violations).
+
+Arguments
+ pOpenInfo pointer to the OPEN_INFO structure
+
+--*/
+{
+ DWORD status;
+ PADAPTER_INFO pAdapterInfo;
+
+ pAdapterInfo = (PADAPTER_INFO )pOpenInfo->adapt_info_ptr;
+ pAdapterInfo->Closing = TRUE;
+
+ //
+ // Close adapter, release resources
+ //
+ if ( !AdapterInit( pOpenInfo, LLC_DIR_CLOSE_ADAPTER, 0)) {
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->SfrReceiveThreadHandle != NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "RplDlcTerm: Wait( Sfr)"));
+ status = WaitForSingleObject( pAdapterInfo->SfrReceiveThreadHandle, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ RplDump( ++RG_Assert,( "status=%d", status==WAIT_FAILED ? GetLastError() : status));
+ }
+ }
+
+ if ( pAdapterInfo->EtherStartThreadHandle != NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_INIT,( "RplDlcTerm: Wait( Ether)"));
+ status = WaitForSingleObject( pAdapterInfo->EtherStartThreadHandle, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ RplDump( ++RG_Assert,( "status=%d", status==WAIT_FAILED ? GetLastError() : status));
+ }
+ }
+
+ return( TRUE);
+}
+
+
diff --git a/private/net/svcdlls/rpl/dll/local.h b/private/net/svcdlls/rpl/dll/local.h
new file mode 100644
index 000000000..48bfb1539
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/local.h
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file used by rpldll & rploem code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+ 19-Nov-1993 vladimv
+ rpldll.h -> local.h + cleanup & face-lift
+
+--*/
+
+#define NOTHING // same def in ntdef.h which is not included here
+
+#include <nt.h> // NT definitions
+#include <ntrtl.h> // NT runtime library definitions
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+
+//#include <lmcons.h> // LAN Manager common definitions
+#include <lmerr.h> // LAN Manager network error definitions
+#include <lmsname.h> // LAN Manager service names
+#include <lmapibuf.h> // NetApiBufferFree
+
+#include <netlib.h> // LAN Man utility routines
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <netdebug.h> // NetpDbgPrint
+#include <tstring.h> // Transitional string functions
+#include <icanon.h> // I_Net canonicalize functions
+#include <align.h> // ROUND_UP_COUNT macro
+
+#include <services.h> // LMSVCS_GLOBAL_DATA
+#include <apperr.h> // APE_AT_ID_NOT_FOUND
+
+#include <dlcapi.h> // - LLC_CCB
+#include <dlcio.h> // - DLC_DO_NOT_CHAIN_XMIT
+#include <lmerrlog.h> // - ERRLOG_BASE NELOG_OEM_Code
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <rpldebug.h>
+#include <rpldll.h> // rplnet.dll entry points & return codes
+#include <riplcons.h>
+#include <ripltyps.h>
+
+
+//
+// BUGBUG For Ethernet, DLC driver returns 0x100 while LLC_ADAPTER_ETHERNET
+// BUGBUG is 0x10. One or the other should be changed. The following
+// BUGBUG defines & "<<4" are introduced as a workaround.
+//
+#define RPL_ADAPTER_ETHERNET (LLC_ADAPTER_ETHERNET<<4)
+#define RPL_ADAPTER_TOKEN_RING LLC_ADAPTER_TOKEN_RING
+//
+// BUGBUG -- DLC uses a "currently free value" to indicate FDDI.
+//
+#define RPL_ADAPTER_FDDI 0x0200
+
+
+#define LOAD_SAP 0xF8 // Receive SAP for SEND.FILE.REQUEST frames
+#define FIND_SAP 0xFC // Receice SAP for FIND frames
+
+//
+// Error handler definitions and return codes to server
+//
+
+
+//
+// Non DLC causing error. Note that these do NOT and should NOT overlap
+// with dlcapi.h command codes.
+//
+#define RPLDLL_NO_ERROR (0xFF)
+#define SEM_SET (RPLDLL_NO_ERROR - 1) // ResetEvent
+#define SEM_WAIT (RPLDLL_NO_ERROR - 2) // WaitForSingle
+#define SEM_CREATE (RPLDLL_NO_ERROR - 3) // CreateEvent
+#define THREAD_CREATE (RPLDLL_NO_ERROR - 5) // CreateThread
+
+// Message number definitions. See RMTDLL.MSG file
+
+// SEM_ , ACSLAN_, MEMORY_, THREAD_ERROR
+#define FAIL_MSG 110 // RMT0110I: RMTNET2.DLL: %1 failed.
+
+// SEM_ , MEMORY_, and THREAD_ERROR
+#define DOS_ERROR 111 // RMT0111I: The error code is the data.
+
+#define TOO_SMALL_XMIT_BUFF 120
+
+// RMTNET1.DLL errors
+
+#define CANNOT_LOAD_MOD 125
+#define PROC_NOT_FOUND 126
+#define VERSION_MISMATCH 127
+
+
+#define MSG_FILENAME "RPLDLL.MSG"
+
+
+// BYTE => BYTE
+// INT => SHORT
+// UINT => WORD
+
+//
+// COMPLETE_BY_READ is used for LLL_RECEIVE in "ulReceivedFlag".
+// Apparently, any other non-zero value would do equally well.
+//
+#define COMPLETE_BY_READ 1
+
+//
+// Version number returned to server ==
+// Version number returned by RPL2_Init
+//
+#define VERSION2_00 0x0200
+
+//
+// Helper Macros
+//
+
+// Swap bytes of word: (B1-B0) -> (B0-B1)
+//
+#define HILO(w) ((WORD)(((w<<8) | (w>>8))))
+
+
+// Swap bytes and words of dword: (B3-B2-B1-B0) -> (B0-B1-B2-B3)
+//
+#define HILO2(_x) ((((DWORD)HILO(HIWORD(_x))) | (((DWORD)HILO(LOWORD(_x)))<<16)))
+
+
+// SAP receive buffer pool values
+#define SAP_BUFFER_SIZE 240
+#define FIND_POOL_SIZE SAP_BUFFER_SIZE
+#define LOAD_POOL_SIZE (20*SAP_BUFFER_SIZE) // 20 rcv buffers for load SAP
+#define PARAGRAPH 16
+#define LOAD_POOL_SIZE_PARA (LOAD_POOL_SIZE/PARAGRAPH)
+#define FIND_POOL_SIZE_PARA (FIND_POOL_SIZE/PARAGRAPH)
+
+// Type of buffer for BUFFER.FREE
+#define FIND_FREE 0
+#define SFR_FREE 1
+
+// RMT2 internal data structures and RIPL frame formats
+// Size of ADAPTER_INFO structure MUST NOT exceed 2 kbytes
+
+
+typedef struct _ADAPTER_INFO {
+ BYTE Closing; // TRUE if adapter has been closed
+ BYTE adapter_number; // number of adapter to be used 0,1...
+ WORD network_type; // TokenRing or Ethernet
+ WORD loadsid; // Station ID for LOAD SAP
+ WORD findsid; // Station ID for FIND SAP
+ WORD max_xmit_buff_size; // Upper limit for buffers to xmit
+ HANDLE gen_sem; // event for DLC general work
+ HANDLE findsem; // event for FIND_SAP RECEIVE and BUFFER.FREE
+ HANDLE sfrsem; // event for LOAD_SAP RECEIVE
+ HANDLE getdata_sem;// event for LOAD_SAP READ and BUFFER.FREE
+
+ HANDLE hBufferPool;// DLC handle used for buffer supplied to DLC
+
+ //
+ // Following data is not actual adapter info,
+ // but this is only place to hide it to make
+ // these DLLs HANDLE-CALLABLE ie. RPL_Init must be made to
+ // every network adapter that is used. Adapter_info is
+ // so called handle, that must be used on subsequent calls
+ // to that adapter.
+ //
+
+ //
+ // SfrReceiveThread is used to keep receive posted for SFR.
+ // SfrReceiveThreadHandle is NULL if & only this thread exists.
+ //
+ HANDLE SfrReceiveThreadHandle;
+
+ //
+ // EtherStartThread is used to send DLC to 3Com 3Station clients.
+ // EtherStartThreadHandle is non-NULL if & only this thread exists.
+ //
+ HANDLE EtherStartThreadHandle;
+
+ //
+ // Used by SfrReceiveThread to let DlcSfr thread it is time to exit.
+ //
+ BOOL SfrReturn;
+
+ //
+ // Command control block and parameter tables for open adapter, open sap,
+ // set functional address, get status and close adapter commands.
+ //
+
+ // Note: re-used data space
+ struct {
+ LLC_CCB ccb; // the base ccb
+ union {
+ struct {
+ LLC_BUFFER_CREATE_PARMS bcp; // Buffer Create Parms
+ } s0;
+ struct {
+ LLC_DIR_OPEN_ADAPTER_PARMS cpo; // Ccb Parms Open
+ LLC_ADAPTER_OPEN_PARMS oap; // Open Adapter Parms
+ LLC_EXTENDED_ADAPTER_PARMS oep; // Open Extended Parms
+ LLC_DLC_PARMS odp; // Open Dlc Parms
+ } s1;
+ struct {
+ LLC_DIR_STATUS_PARMS dsp; // Dir Status Parms
+ } s2;
+ struct {
+ LLC_DLC_OPEN_SAP_PARMS cpo; // Ccb ???
+ } s3;
+ } u;
+ } s;
+ // CCBs and parameter tables for RECEIVEs, READ and BUFFER_FREE
+ struct {
+ LLC_CCB r; // RECEIVE ccb for find
+ LLC_RECEIVE_PARMS r1; // parameter table 17 bytes
+ BYTE pad; // alignment
+ } u1;
+
+ struct {
+ LLC_CCB r; // RECEIVE ccb for sfr
+ LLC_RECEIVE_PARMS r1; // parameter table 17 bytes*/
+ BYTE pad; // alignment
+ } u2;
+
+ struct {
+ LLC_CCB r; // READ ccb for sfr
+ LLC_READ_PARMS r1; // parameter table
+ } u3;
+
+ //
+ // BUFFER.FREE CCB and parameter table for freeing received buffer,
+ // SEND.FILE.REQUEST frames
+ //
+ LLC_CCB sfr_bf_ccb;
+ LLC_BUFFER_FREE_PARMS sfr_bf_ccbpt;
+
+ //
+ // BUFFER.FREE CCB and parameter table for freeing received buffer,
+ // FIND frames
+ //
+ LLC_CCB find_bf_ccb;
+ LLC_BUFFER_FREE_PARMS find_bf_ccbpt;
+
+} ADAPTER_INFO, *PADAPTER_INFO;
+
+
+typedef struct _LAN_HEADER {
+ BYTE pcf0; // Physical control field 0
+ BYTE pcf1; // Physical control field 1
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER;
+
+//
+// In LAN_RESOURCE structure below it is essential that LanHeader field
+// is right behind XmitBuffer field. LLC_XMIT_BUFFER structure used to have
+// a trailing auchData[1] which would give a wrong offset for LAN_RESOURCE.
+// Now it has a trailing auchData[0] so compiler complains if it is not the
+// the last element in a tructure. Both problems are solved by defining
+// XMIT_BUFFER which has no auchData[] field.
+//
+
+typedef struct _XMIT_BUFFER {
+ struct _XMIT_BUFFER * pNext; // next buffer (or NULL)
+ WORD usReserved1; //
+ WORD cbBuffer; // length of transmitted data
+ WORD usReserved2; //
+ WORD cbUserData; // length of optional header
+} XMIT_BUFFER, *PXMIT_BUFFER;
+
+typedef struct _LAN_RESOURCE {
+ BYTE xmit_error_cnt; // consecutive xmit error count
+ BYTE retry_count; // xmit retries on current frame
+ LLC_CCB ccb; // for RECEIVE ,TRANSMIT frames
+ LLC_TRANSMIT_PARMS TransmitParms; // parameter block for TRANSMIT
+ XMIT_BUFFER XmitBuffer; // buffer one header
+ LAN_HEADER LanHeader; // for TRANSMIT.UI.FRAME
+ BYTE rest_of_routing[16]; // lan hdr defines 2 bytes routing
+ BYTE frame[110]; // the frame itself
+} LAN_RESOURCE, *PLAN_RESOURCE;
+
+
+//
+// Remote boot structures cannot use default packing. Thus:
+//
+
+#include <packon.h> // Pack structures: #pragma pack(1)
+
+// The find frame structure is defined below.
+
+typedef struct _FIND_FRAME {
+ WORD program_length; // 0x53 + value in first 2 bytes of file_hdr
+ WORD program_command; // must be FIND_CMD
+ DWORD corr_hdr;
+ DWORD correlator;
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD lsap_hdr;
+ BYTE rsap;
+ DWORD search_hdr;
+ DWORD loader_hdr;
+ BYTE mach_conf[8];
+ WORD equip_flags;
+ WORD memory_size;
+ BYTE rpl_ec[8];
+ WORD adapter_id;
+ BYTE adapter_ec[10];
+ DWORD file_hdr;
+ BYTE file_name[1];
+} FIND_FRAME, *PFIND_FRAME;
+
+#define FIND_LEN 0x53
+
+//
+// The following structure defines the FOUND frame,
+// RMT2 transmits the FOUND frame in response to a FIND
+// frame from a workstation already configured to RIPL.
+//
+
+typedef struct _FOUND_FRAME {
+ WORD program_length;
+ WORD program_command;
+ DWORD corr_header;
+ DWORD correlator;
+ DWORD resp_hdr;
+ BYTE resp_code;
+ DWORD dest_hdr; // NOTE 4 bytes! not 6
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH];
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD lsap_hdr;
+ BYTE rsap;
+} FOUND_FRAME, *PFOUND_FRAME;
+
+
+//
+//
+// The fdr structure defines the FILE.DATA.RESPONSE frame transmitted by RMT2
+// in response to a validated SEND.FILE.DATA frame already received by RMT2.
+//
+
+
+typedef struct _FDR { // FILE.DATA.RESPONSE
+ WORD program_length;
+ WORD program_command;
+ DWORD seq_hdr;
+ DWORD seq_num;
+ DWORD loader_hdr;
+ DWORD locate_addr;
+ DWORD xfer_addr;
+ BYTE flags;
+ DWORD data_hdr; // the data follows this
+} FDR, *PFDR;
+
+
+//
+// The sfr structure defines the SEND.FILE.REQUEST frame transmitted by
+// the remote workstation after reception of the FOUND frame.
+//
+
+typedef struct _SFR { // SEND.FILE.REQUEST
+ WORD program_length;
+ WORD program_command;
+ DWORD seq_header; // sequence number vector
+ DWORD seq_num; // starting sequence number
+ DWORD info_hdr;
+ DWORD frame_hdr;
+ WORD max_frame;
+ DWORD class_hdr;
+ WORD conn_class;
+ DWORD source_hdr;
+ BYTE source_addr[ NODE_ADDRESS_LENGTH];
+ DWORD lsap_hdr;
+ BYTE rsap;
+ DWORD search_hdr;
+ DWORD loader_hdr;
+ BYTE mach_conf[8];
+ WORD equip_flags;
+} SFR, *PSFR;
+
+#include <packoff.h> // Restore default packing, #pragma pack()
+
+//
+// The following structure defines the Receive frame from a requesting
+// device seeking a RIPL server. The find_frame occupies the second
+// half of the structure (or the SEND.FILE.REQUEST frame, or ALERT frame).
+// The only field used by RMT2 to determine the
+// frame type is the "program_command" field of the frame.
+//
+
+
+typedef struct _RCVBUF {
+ LLC_NOT_CONTIGUOUS_BUFFER b; // buffer one header
+ union {
+ FIND_FRAME Find; // the data (find frame)
+ SFR SendFileRequest; // or a SEND.FILE.REQUEST
+ } u;
+} RCVBUF, *PRCVBUF;
+
+
+#define RCB_ALERT 0xff // rcb was alerted
+#define RCB_ALERTED 0x1 // bit set for ALERT frame
+#define RCB_ERROR 0x1 // bit for tx-rx error
+
+//
+// The following #defines relate to the values of the program_command
+// field in the frame structures above
+//
+
+#define FIND_CMD 0x0100 // FIND frame
+#define SEND_CMD 0x1000 // SEND command
+#define FDR_CMD 0x2000 // FILE.DATA.RESPONSE cmd
+#define ALERT_CMD 0x3000 // ALERT frame
+#define FOUND_CMD 0x0200 // FOUND frame
+
+//
+// The following #defines relate the the values of various fields in
+// the FOUND frame structure.
+//
+
+#define OVRHD 123 // overhead on a FOUND frame
+#define CLASS1 0x100 // class 1 provided by RJS
+#define FL 0x3A00 // sizeof found frame
+#define CORR_HDR 0x03400800 // NOT intel format
+#define RESP_HDR 0x0b400500 // ditto
+#define DEST_HDR 0x0c400a00 // ditto
+#define SOURCE_HDR 0x06400a00 // ditto
+#define INFO_HDR 0x08001000 // NOT intel format
+#define FRAME_HDR 0x09400600 // LAN format
+#define CLASS_HDR 0x0a400600 // LAN format
+#define LSAP_HDR 0x07400500 // LAN format
+#define BRDCST 0x82 // routing for broadcast
+#define UNSPEC_FRM_SIZE 0x70 // (DFFF rrrr) FFF frm size bits ON
+#define SEQ_HDR 0x11400800
+#define LDR_HDR 0x14c00d00
+#define DATA_HDR 0x18400000
+#define MAXRETRY 5 // retry TRANSMIT this much
+
+//
+// We do not write to Net error log until there is at least
+// MAX_CONSECUTIVE_ERROR transmit errors.
+//
+#define MAX_CONSECUTIVE_ERROR 3
+
+// Definitions for volume_id handling
+#define MAX_VOLID_LEN 17
+#define VOLID_FIELD1 0xFF
+#define VOLID_FIELD2 0xB0
+
+
+//
+// Routines exported by report.c
+//
+VOID RplDlcReportEvent(
+ DWORD ErrorCode,
+ DWORD Command
+ );
+
+//
+// Routines exported by buffer.c
+//
+BOOL RplDlcBufferFree(
+ BYTE free_type,
+ PADAPTER_INFO pAdapterInfo
+ );
+
+VOID ReverseBits( PBYTE NodeAddress );
diff --git a/private/net/svcdlls/rpl/dll/makefile b/private/net/svcdlls/rpl/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/dll/report.c b/private/net/svcdlls/rpl/dll/report.c
new file mode 100644
index 000000000..3eb7520e2
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/report.c
@@ -0,0 +1,60 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Event log writing function used in rpl "dll".
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+#include <jet.h> // need to include because rpllib.h depends on JET
+#include <rpllib.h> // RplReportEvent()
+
+VOID RplDlcReportEvent(
+ DWORD ErrorCode,
+ DWORD Command
+ )
+/*++
+
+Routine Description:
+ Composes an error message and writes it to a LAN Manager error log file.
+
+Arguments:
+ ErrorCode actual error code
+ command failed Dos- or ACSLAN command
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD RawDataBuffer[ 2];
+
+ RawDataBuffer[ 0] = Command; // dlc command or internal rpl command
+ RawDataBuffer[ 1] = ErrorCode; // dlc error code or win32 error code
+
+ RplReportEvent(
+ NELOG_System_Error,
+ NULL,
+ sizeof( RawDataBuffer),
+ (PBYTE)RawDataBuffer
+ );
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/dll/sfr.c b/private/net/svcdlls/rpl/dll/sfr.c
new file mode 100644
index 000000000..0084f297c
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sfr.c
@@ -0,0 +1,499 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ sfr.c
+
+Abstract:
+
+ Contains RplDlcSfr() entry point.
+ Contains RplFindRcb() function which is also used by server\request.c\RequestThread().
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+
+
+VOID SfrReceiveThread( PADAPTER_INFO pAdapterInfo)
+/*++
+
+Routine Description:
+
+ Created from RplDlcSfr(). Keeps RECEIVE posted on LOAD_SAP.
+ Received data is read by RpldSendFileRequest() function using AcsLan
+ READ command. The RECEIVE call will complete if ...
+
+--*/
+{
+ PLLC_CCB pBadCcb;
+ DWORD status;
+ BYTE command; // failed command
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "++SfrReceiveThread: pAdapterInfo=0x%x", pAdapterInfo));
+
+ memset( (PVOID)&pAdapterInfo->u2, '\0', sizeof(pAdapterInfo->u2));
+ pAdapterInfo->u2.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u2.r.uchDlcCommand = LLC_RECEIVE;
+ pAdapterInfo->u2.r.hCompletionEvent = pAdapterInfo->sfrsem;
+ pAdapterInfo->u2.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u2.r1;
+ pAdapterInfo->u2.r1.ulReceiveFlag = COMPLETE_BY_READ;
+ pAdapterInfo->u2.r1.usStationId = pAdapterInfo->loadsid;
+
+ for ( ; ;) {
+ status = AcsLan(&pAdapterInfo->u2.r, &pBadCcb);
+ if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ command = LLC_RECEIVE;
+ break;
+ }
+ //
+ // Waiting for SEND.FILE.REQUEST
+ //
+ status = WaitForSingleObject( pAdapterInfo->sfrsem, INFINITE);
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ command = SEM_WAIT;
+ break;
+ }
+ if ( pAdapterInfo->u2.r.uchDlcStatus
+ != LLC_STATUS_LOST_DATA_NO_BUFFERS
+ && pAdapterInfo->u2.r.uchDlcStatus
+ != LLC_STATUS_LOST_DATA_INADEQUATE_SPACE) {
+ status = pAdapterInfo->u2.r.uchDlcStatus;
+ command = LLC_RECEIVE;
+ break;
+ }
+ }
+
+ if ( pAdapterInfo->Closing == TRUE) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "--SfrReceiveThread: pAdapterInfo=0x%x", pAdapterInfo));
+ return;
+ }
+
+ RplDump( ++RG_Assert,( "status=%d, command=0x%x", status, command));
+
+ //
+ // We come here in case of unrecoverable errors. RPL service will
+ // be terminated. We also set getdata_sem event to unblock
+ // RpldSendFileRequest() thread.
+ //
+ RplDlcReportEvent( status, command);
+ pAdapterInfo->SfrReturn = TRUE;
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+}
+
+
+VOID set_fdr_fixed(
+ PLAN_RESOURCE pLanResource,
+ WORD usStationId
+ )
+/*++
+Routine Description:
+ set_fdr_fixed() initializes the fixed parts of the FILE.DATA.RESPONSE
+ frame and the TRANSMIT.UI parameter block. It takes a pointer to the
+ LAN RCB containing the frame and transmit parameters to be initialized
+ and usStationId for architected FIND SAP.
+--*/
+
+{
+ PFDR pFdr; // point to frame
+ LLC_TRANSMIT_PARMS *tpp; // point to xmit parms
+
+ pFdr = (PFDR)pLanResource->frame; // generate frame here
+ tpp = &pLanResource->TransmitParms; // locate xmit parms
+ // setup fixed part of frame
+ // program length varies
+ pFdr->program_command = FDR_CMD; // 00 20
+ pFdr->seq_hdr = SEQ_HDR; // 00 08 40 11
+ // seq num varies
+ pFdr->loader_hdr = LDR_HDR; // 00 0d c0 14
+ pFdr->locate_addr = 0; // 00 00 00 00
+ pFdr->xfer_addr = 0; // 00 00 00 00
+ pFdr->flags = 0; // flags zero = no FDR ack
+ // data_hdr varies
+ pFdr->data_hdr = DATA_HDR; // 00 ?? 40 18
+ // setup transmit parm block
+ tpp->usStationId = usStationId;
+ tpp->uchTransmitFs = 0; // 00
+ tpp->uchRemoteSap = FIND_SAP; // FC
+ // Queue #1 is 1st buffer
+ tpp->pXmitQueue1 = (PLLC_XMIT_BUFFER)&pLanResource->XmitBuffer;
+ tpp->pXmitQueue2 = NULL; // 00 00 00 00
+ tpp->cbBuffer1 = (sizeof *pFdr); // 00 1D
+ tpp->pBuffer1 = (PCH)pLanResource->frame;
+ tpp->uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT; // don't chain on completion
+}
+
+
+VOID ProcessSfr(
+ PRCB pRcb,
+ PADAPTER_INFO pAdapterInfo,
+ PRCVBUF pRcvbuf
+ )
+{
+
+ PLAN_RESOURCE pLanResource;
+ WORD tempword;
+
+ pLanResource = (PLAN_RESOURCE)pRcb->lan_rcb_ptr;
+ pLanResource->retry_count = 0;
+ tempword = HILO(pRcvbuf->u.SendFileRequest.max_frame);
+ //
+ // Both protocol data and boot block data must fit in
+ // transmit buffer frame (OVRHD == size of protocol data).
+ //
+ if (tempword <= pAdapterInfo->max_xmit_buff_size) {
+ tempword -= OVRHD;
+ } else {
+ tempword = (WORD)( pAdapterInfo->max_xmit_buff_size - OVRHD);
+ }
+
+ //
+ // "max_frame" is used by rpl server to determine the size of boot
+ // block data frames.
+ //
+ pRcb->max_frame = tempword;
+
+ memcpy( pLanResource->LanHeader.dest_addr,
+ &(pRcvbuf->b.auchLanHeader[ 8]), NODE_ADDRESS_LENGTH);
+
+ // mask off routing info indication bit
+ pLanResource->LanHeader.dest_addr[0] &= 0x7F;
+ // note: byte to word xlate
+ pLanResource->XmitBuffer.cbBuffer = pRcvbuf->b.cbLanHeader;
+
+
+ if (pAdapterInfo->network_type == RPL_ADAPTER_TOKEN_RING) {
+ if ( pRcvbuf->b.cbLanHeader > 14) {
+ //
+ // There is routing info, we have TokenRing case. Inherit
+ // the routing from received frame. Note memcpy length! Also,
+ // we "and" with 0x1f for "non broadcast" and "xor" with 0x80
+ // for "direction interpreted from right to left".
+ //
+ memcpy( pLanResource->LanHeader.routing_info_header,
+ &pRcvbuf->b.auchLanHeader[14], pRcvbuf->b.cbLanHeader -14);
+ pLanResource->LanHeader.routing_info_header[0] &= 0x1f;
+ pLanResource->LanHeader.routing_info_header[1] ^= 0x80;
+ } else {
+ //
+ // There is no routing info, we have Etherner case. We just
+ // clear the indication bit.
+ //
+ pLanResource->LanHeader.source_addr[0] &= 0x7f;
+ }
+ }
+
+ // set FILE.DATA.RESPONSE
+ set_fdr_fixed( pLanResource, pAdapterInfo->findsid);
+}
+
+
+PRCB RplFindRcb(
+ PPRCB HeadRcbList,
+ PBYTE NodeAddress
+ )
+/*++
+Routine Description:
+
+ Finds matching rcb from rcbs in use chain.
+
+Arguments:
+ HeadRcbList pointer to the variable, which contains pointer to the
+ first RCB in chain
+ NodeAddress pointer to the hex adapter-id to scan for
+
+Return Value:
+ Pointer to matching RCB if success, NULL otherwise.
+
+--*/
+{
+ PRCB pRcb;
+
+ DebugCheckList( HeadRcbList, NULL, 0);
+
+ for ( pRcb = *HeadRcbList; pRcb != NULL; pRcb = pRcb->next_rcb) {
+ if ( !memcmp( pRcb->NodeAddress, NodeAddress,
+ sizeof( pRcb->NodeAddress) )) {
+ break;
+ }
+ }
+ return( pRcb);
+
+}
+
+
+BOOL RplDlcSfr(
+ POPEN_INFO pOpenInfo,
+ PPRCB HeadRcbList,
+ PCRITICAL_SECTION ProtectRcbList
+ )
+/*++
+
+Routine Description:
+ Receives SEND.FILE.REQUEST frames. Creates a thread to keep a receive
+ command outstanding on the opened LOAD_SAP in order to pick up
+ SEND.FILE.REQUEST frames from workstations. Uses AcsLan READ-function
+ to get notification for received data.
+
+ It validates the frame and places the requested sequence number,
+ lan header, transmit buffer header and parameters into the RCB
+ associated with the workstation and clears RAM semaphore (in RCB)
+ to wake up the service thread for the workstation.
+ It then frees the buffer containing the frame and waits for the next one.
+
+Arguments:
+ pOpenInfo pointer to the OPEN_INFO structure
+ HeadRcbList pointer to the variable, which contains pointer to the
+ first RCB in chain. The chain contains RCB-s of all clients this
+ server is interested in getting SEND.FILE.REQUEST frames from.
+ ProtectRcbList for serialising access to rcb list
+
+Return Value:
+ ERROR_SUCCESS if success, non-zero otherwise
+--*/
+{
+ DWORD status;
+ PLLC_CCB pBadCcb;
+ PRCB pRcb;
+ PRCVBUF pRcvbuf;
+ DWORD sfr_seq_number; // in the incoming SFR frame
+ DWORD fdr_seq_number; // of what worker already sent
+ PADAPTER_INFO pAdapterInfo;
+ DWORD ThreadId;
+
+ pAdapterInfo = (PADAPTER_INFO)pOpenInfo->adapt_info_ptr;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR, (
+ "++SendFileRequest: pAdapterInfo=0x%x", pAdapterInfo));
+
+ //
+ // Create thread to keep receive posted.
+ //
+ pAdapterInfo->SfrReceiveThreadHandle = CreateThread(
+ NULL, // lpThreadAttribute
+ 0, // dwStackSize - same as first thread
+ (LPTHREAD_START_ROUTINE)SfrReceiveThread, // lpStartAddress
+ (LPVOID)pAdapterInfo, // lpParameter
+ 0, // dwCreationFlags
+ &ThreadId // lpThreadId
+ );
+ if ( pAdapterInfo->SfrReceiveThreadHandle == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, THREAD_CREATE);
+ return( FALSE);
+ }
+
+ //
+ // Setup CCB for LLC_READ, then setup LLC_READ_PARMS to wake up
+ // only when data arrives.
+ //
+ memset( (PVOID)&pAdapterInfo->u3, '\0', sizeof(pAdapterInfo->u3));
+ pAdapterInfo->u3.r.uchAdapterNumber = pAdapterInfo->adapter_number;
+ pAdapterInfo->u3.r.uchDlcCommand = LLC_READ;
+ pAdapterInfo->u3.r.hCompletionEvent = pAdapterInfo->getdata_sem;
+ pAdapterInfo->u3.r.u.pParameterTable = (PLLC_PARMS)&pAdapterInfo->u3.r1;
+
+ pAdapterInfo->u3.r1.usStationId = pAdapterInfo->loadsid;
+ pAdapterInfo->u3.r1.uchOptionIndicator = LLC_OPTION_READ_SAP;
+ pAdapterInfo->u3.r1.uchEventSet = LLC_EVENT_RECEIVE_DATA;
+
+ //
+ // Loop for ever processing SendFileRequest frames arriving to this
+ // adapter.
+ //
+ for ( ; ;) {
+
+ //
+ // Reset event to be cleared later on by AcsLan.
+ //
+ if ( !ResetEvent( pAdapterInfo->getdata_sem)) {
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+ status = GetLastError();
+ RplDump( ++RG_Assert,( "pAdapterInfo=0x%x, status=%d", pAdapterInfo, status));
+ RplDlcReportEvent( status, SEM_SET);
+ return( FALSE);
+ }
+
+ status = AcsLan( &pAdapterInfo->u3.r, &pBadCcb);
+ if ( pAdapterInfo->Closing == TRUE) {
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+ return( FALSE);
+ }
+
+ if (status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ RplDump( ++RG_Assert,( " r=0x%x, status=%d", &pAdapterInfo->u3.r, status));
+ RplDlcReportEvent( status, LLC_READ); // failed to READ
+ (VOID)SetEvent( pAdapterInfo->getdata_sem);
+ return( FALSE);
+ }
+
+ status = WaitForSingleObject( pAdapterInfo->getdata_sem, INFINITE);
+ if ( pAdapterInfo->Closing == TRUE) {
+ return( FALSE);
+ }
+
+ if ( ( status != WAIT_OBJECT_0) || (pAdapterInfo->u3.r.uchDlcStatus)) {
+ if ( status != WAIT_OBJECT_0) {
+ if ( status == WAIT_FAILED) {
+ status = GetLastError();
+ }
+ RplDlcReportEvent( status, SEM_WAIT);
+ } else {
+ RplDlcReportEvent( pAdapterInfo->u3.r.uchDlcStatus, LLC_READ);
+ }
+ RplDump( ++RG_Assert,( " pAdapterInfo=0x%x, status=%d, DlcStatus=0x%x",
+ pAdapterInfo, status, pAdapterInfo->u3.r.uchDlcStatus));
+ return( FALSE);
+ }
+
+ if ( pAdapterInfo->SfrReturn) { // Check sfr receive
+ //
+ // SfrReceiveThreadHandle is being waited in DlcTerm() call.
+ //
+ return( TRUE); // event was written by sfr receive
+ }
+
+ //
+ // Point to receive buffer.
+ //
+ pRcvbuf = (PRCVBUF)(pAdapterInfo->u3.r1.Type.Event.pReceivedFrame);
+ RplDump( TRUE, ("pRcvbuf=0x%x", pRcvbuf));
+
+
+ //
+ // Whenever we find a matching RCB we set SFR flag so that worker
+ // thread does not move this RCB while we are working on it. Note
+ // that this means that SFR must be cleared once we are done with
+ // this RCB.
+ //
+
+ EnterCriticalSection( ProtectRcbList);
+
+ pRcb = RplFindRcb( HeadRcbList, (PBYTE)pRcvbuf->u.SendFileRequest.source_addr);
+ if ( pRcb != NULL) {
+ if ( pRcb->Pending == TRUE) {
+ pRcb = NULL; // pretend we did not see this RCB
+ } else {
+ pRcb->SFR = TRUE; // hold this RCB
+ }
+ }
+
+ LeaveCriticalSection( ProtectRcbList);
+
+ if ( pRcb == NULL
+ || pRcvbuf->u.SendFileRequest.program_command == ALERT_CMD
+ || pRcvbuf->u.SendFileRequest.program_command != SEND_CMD
+ || pRcvbuf->u.SendFileRequest.conn_class != CLASS1) {
+ //
+ // Calling adapter was not found, or we received an alert frame,
+ // or we received an unknown frame, or request class was wrong.
+ //
+
+ if ( pRcb != NULL && status == 0
+ && pRcvbuf->u.SendFileRequest.program_command == ALERT_CMD) {
+ //
+ // Got an alert frame on LOAD_SAP and RCB is found.
+ //
+ pRcb->flags.alerted = RCB_ALERTED;// yes: signal
+ //
+ // Instead of ACK or rexmit request, we have Alert frame.
+ // Wake up worker.
+ //
+ if ( !SetEvent( pRcb->SF_wakeup)) {
+ RplDump( ++RG_Assert,("pRcb=0x%x error=%d", pRcb, GetLastError()));
+ }
+ }
+ if ( pRcb != NULL) {
+ pRcb->SFR = FALSE; // release RCB
+ }
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ continue;
+ }
+
+ //
+ // Get SFR sequence number (in intel format) & validate it.
+ //
+ sfr_seq_number = HILO2(pRcvbuf->u.SendFileRequest.seq_num);
+ fdr_seq_number = pRcb->fdr_seq_number;
+ if ( sfr_seq_number > fdr_seq_number + 1) {
+ RplDump( ++RG_Assert, (
+ "pRcb=0x%x, sfr_seq_number=%d, fdr_seq_number=%d",
+ pRcb, sfr_seq_number, fdr_seq_number));
+ pRcb->SFR = FALSE; // release RCB
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ continue;
+ }
+
+ //
+ // We received a meaningful SFR frame. Initialize data if this is
+ // the first SFR frame. Then update clients sequence number & unblock
+ // worker thread.
+ //
+
+ if ( pRcb->ReceivedSfr == FALSE) {
+ pRcb->ReceivedSfr = TRUE;
+ ProcessSfr( pRcb, pAdapterInfo, pRcvbuf);
+ }
+ pRcb->sfr_seq_number = sfr_seq_number;
+
+ if ( !SetEvent( pRcb->SF_wakeup)) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x error=%d", pRcb, GetLastError()));
+ }
+
+ pRcb->SFR = FALSE; // release RCB
+
+ if ( !RplDlcBufferFree( SFR_FREE, pAdapterInfo)) {
+ return( FALSE);
+ }
+ } // end of big loop
+}
+
+
+#ifdef RPL_DEBUG
+VOID DebugCheckList( PPRCB HeadList, PRCB pInputRcb, DWORD Operation)
+{
+ PRCB pRcb;
+ DWORD count;
+ DWORD found;
+
+ for ( pRcb = *HeadList, count = 0, found = FALSE; pRcb != NULL;
+ pRcb = pRcb->next_rcb, count++) {
+ if ( count > 20) {
+ RplDump( ++RG_Assert,("Circular list"));
+ }
+ if ( pRcb == pInputRcb) {
+ found = TRUE;
+ }
+ }
+ if ( pInputRcb != NULL) {
+ if ( Operation == RPL_MUST_FIND && found == FALSE) {
+ RplDump( ++RG_Assert,("Element not present"));
+ } else if ( Operation == RPL_MUST_NOT_FIND && found == TRUE) {
+ RplDump( ++RG_Assert,("Element already present"));
+ }
+ }
+}
+#endif
+
diff --git a/private/net/svcdlls/rpl/dll/sizeof.c b/private/net/svcdlls/rpl/dll/sizeof.c
new file mode 100644
index 000000000..501dba2cf
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sizeof.c
@@ -0,0 +1,174 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ sizeof.c
+
+Abstract:
+
+ Prints offsets of more commonly used fields in RPL data structures.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include <ntcsrsrv.h>
+#include <stdio.h>
+
+typedef struct _HEAP_ENTRY {
+ //
+ // This field gives the size of the current block in allocation
+ // granularity units. (i.e. Size << HEAP_GRANULARITY_SHIFT
+ // equals the size in bytes).
+ //
+
+ USHORT Size;
+
+ //
+ // This field gives the size of the previous block in allocation
+ // granularity units. (i.e. PreviousSize << HEAP_GRANULARITY_SHIFT
+ // equals the size of the previous block in bytes).
+ //
+
+ USHORT PreviousSize;
+
+ //
+ // This field contains the index into the segment that controls
+ // the memory for this block.
+ //
+
+ UCHAR SegmentIndex;
+
+ //
+ // This field contains various flag bits associated with this block.
+ // Currently these are:
+ //
+ // 0x01 - block is busy
+ // 0x02 - allocated with VirtualAlloc
+ // 0x04 - busy block contains tail fill/free block contains free fill
+ // 0x08 - last block before uncommitted memory
+ // 0x10 - caller settable
+ // 0x20 - caller settable
+ // 0x40 - caller settable
+ // 0x80 - caller settable
+ //
+ // The high order 4 bits are settable by higher level heap interfaces.
+ // For example, the Win32 heap calls remember the DDESHARE and DISCARDABLE
+ // flags here. The pool manager remembers the pool type here.
+ //
+
+ UCHAR Flags;
+
+ //
+ // This field is for debugging purposes. It will normally contain a
+ // stack back trace index of the allocator for x86 systems.
+ //
+
+ union {
+ USHORT AllocatorBackTraceIndex;
+ struct {
+ UCHAR Index;
+ UCHAR Mask;
+ } FreeListBitIndex;
+ } u0;
+
+ //
+ // Format of the remaining fields depends on whether this is an
+ // busy or free block.
+ //
+
+ union {
+ struct {
+ //
+ // This field contains the number of unused bytes at the end of this
+ // block that were not actually allocated. Used to compute exact
+ // size requested prior to rounding requested size to allocation
+ // granularity. Also used for tail checking purposes.
+ //
+
+ ULONG UnusedBytes : 8;
+
+ //
+ // This field is currently unused, but is intended for storing
+ // any encoded value that will give the that gives the type of object
+ // allocated.
+ //
+
+ ULONG Type : 24;
+
+ //
+ // This field is a 32-bit settable value that a higher level heap package
+ // can use. The Win32 heap manager stores handle values in this field.
+ //
+
+ ULONG Settable;
+ } BusyBlock;
+
+ struct {
+ //
+ // Free blocks use these two words for linking together free blocks
+ // of the same size on a doubly linked list.
+ //
+ LIST_ENTRY FreeList;
+ } FreeBlock;
+ } u;
+
+} HEAP_ENTRY, *PHEAP_ENTRY;
+
+void _CRTAPI1 main( void) {
+ printf("FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num) == %d == 0x%x\n",
+ FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num),
+ FIELD_OFFSET( RCVBUF, u.SendFileRequest.seq_num));
+
+ printf("FIELD_OFFSET( OPEN_INFO, adapt_info_ptr) == %d == 0x%x\n",
+ FIELD_OFFSET( OPEN_INFO, adapt_info_ptr),
+ FIELD_OFFSET( OPEN_INFO, adapt_info_ptr));
+
+ printf("sizeof( ADAPTER_INFO) == %d == 0x%x\n",
+ sizeof( ADAPTER_INFO),
+ sizeof( ADAPTER_INFO));
+ printf("FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer) == %d == 0x%x\n",
+ FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer),
+ FIELD_OFFSET( ADAPTER_INFO, u1.r1.pFirstBuffer));
+ printf("FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame) == %d == 0x%x\n",
+ FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame),
+ FIELD_OFFSET( ADAPTER_INFO, u3.r1.Type.Event.pReceivedFrame));
+
+
+ printf("FIELD_OFFSET( FIND_FRAME, source_addr) == %d == 0x%x\n",
+ FIELD_OFFSET( FIND_FRAME, source_addr),
+ FIELD_OFFSET( FIND_FRAME, source_addr));
+
+ printf("FIELD_OFFSET( RCB, AdapterName) == %d == 0x%x\n",
+ FIELD_OFFSET( RCB, AdapterName),
+ FIELD_OFFSET( RCB, AdapterName));
+ printf("FIELD_OFFSET( RCB, lan_rcb_ptr) == %d == 0x%x\n",
+ FIELD_OFFSET( RCB, lan_rcb_ptr),
+ FIELD_OFFSET( RCB, lan_rcb_ptr));
+
+ printf("FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader) == %d == 0x%x\n",
+ FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader),
+ FIELD_OFFSET( LLC_NOT_CONTIGUOUS_BUFFER, auchLanHeader));
+
+ printf("FIELD_OFFSET( LLC_CCB, u.pParameterTable) == %d == 0x%x\n",
+ FIELD_OFFSET( LLC_CCB, u.pParameterTable),
+ FIELD_OFFSET( LLC_CCB, u.pParameterTable));
+
+ printf("FIELD_OFFSET( CSR_PROCESS, ProcessHandle) == %d == 0x%x\n",
+ FIELD_OFFSET( CSR_PROCESS, ProcessHandle),
+ FIELD_OFFSET( CSR_PROCESS, ProcessHandle));
+
+ printf("sizeof( HEAP_ENTRY) == %d == 0x%x\n",
+ sizeof( HEAP_ENTRY),
+ sizeof( HEAP_ENTRY));
+}
diff --git a/private/net/svcdlls/rpl/dll/sources b/private/net/svcdlls/rpl/dll/sources
new file mode 100644
index 000000000..f79e2d95d
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/sources
@@ -0,0 +1,37 @@
+MAJORCOMP=net
+MINORCOMP=rplnet
+
+TARGETPATH=obj
+TARGETNAME=rplnet
+TARGETTYPE=LIBRARY
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ init.c \
+ ether.c \
+ emulator.c \
+ buffer.c \
+ fdr.c \
+ find.c \
+ found.c \
+ sfr.c \
+ report.c
+
+USE_CRTDLL=1
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+UMTEST=sizeof
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib
+
diff --git a/private/net/svcdlls/rpl/dll/xns.c b/private/net/svcdlls/rpl/dll/xns.c
new file mode 100644
index 000000000..e0b878e20
--- /dev/null
+++ b/private/net/svcdlls/rpl/dll/xns.c
@@ -0,0 +1,520 @@
+/*++
+
+Copyright (c) 1987-93 Microsoft Corporation
+
+Module Name:
+
+ xns.c
+
+Abstract:
+
+ Test program to help debug DIR_OPEN_DIRECT path in DLC driver & RPL service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 03 - February - 1993
+
+Revision History:
+
+ 03-Feb-1993 vladimv
+ Ported to NT
+
+--*/
+
+#include "local.h"
+#define PRINTF( _args) printf _args
+
+//
+// ripl_rom[] is the x86 code we send to ETHERSTART clients. Currently it
+// is exactly 0x429 bytes long.
+//
+BYTE ripl_rom[] = {
+0xFA,0xB8,0x90,0x00,0xBC,0x00,0x02,0x8E,0xD0,0xCD,0x12,0x86,0xC4,0x86,0xC4,0xB1,
+0x06,0xD3,0xE0,0x8D,0x0E,0x3C,0x0B,0x83,0xC1,0x0F,0x51,0xD1,0xE9,0xD1,0xE9,0xD1,
+0xE9,0xD1,0xE9,0x2B,0xC1,0x59,0x8E,0xC0,0x8D,0x36,0x39,0x01,0x8B,0xFE,0x2B,0xCE,
+0xE8,0x3B,0x02,0x50,0xB8,0x39,0x01,0x50,0xCB,0x0E,0x1F,0xB4,0xFB,0xB2,0x80,0x8D,
+0x1E,0x5F,0x04,0xCD,0x13,0x8B,0xF3,0x8D,0x3E,0x8A,0x04,0xA5,0xA5,0xA5,0x8B,0xF3,
+0x8D,0x3E,0xC7,0x04,0xA5,0xA5,0xA5,0x8B,0xF3,0x8D,0x3E,0xF2,0x04,0xA5,0xA5,0xA5,
+0xB4,0x03,0x32,0xFF,0xCD,0x10,0x32,0xD2,0x01,0x16,0xED,0x03,0x01,0x16,0x22,0x04,
+0x01,0x16,0x57,0x04,0xBE,0xC3,0x03,0x8B,0x16,0xED,0x03,0xBB,0x24,0x00,0x90,0xE8,
+0x0B,0x02,0xA1,0xED,0x03,0x05,0x24,0x00,0x90,0xA3,0xED,0x03,0xBE,0xE7,0x03,0x8B,
+0x16,0xED,0x03,0xBB,0x06,0x00,0x90,0xE8,0xF3,0x01,0xBE,0x68,0x00,0x90,0x8D,0x1E,
+0x59,0x04,0xE8,0xDA,0x01,0xBE,0xBE,0x05,0x8D,0x1E,0x2B,0x05,0xE8,0xD7,0x01,0x73,
+0x13,0x0B,0xC0,0x74,0x03,0xE9,0x8D,0x00,0xBE,0xE7,0x03,0xBB,0x06,0x00,0x90,0xE8,
+0xE3,0x01,0xEB,0xC8,0x8D,0x36,0x5B,0x05,0x8D,0x3E,0xC1,0x04,0xA5,0xA5,0xA5,0xBE,
+0xEF,0x03,0x8B,0x16,0x22,0x04,0xBB,0x2F,0x00,0x90,0xE8,0xB0,0x01,0xBE,0x24,0x04,
+0x8B,0x16,0x57,0x04,0xBB,0x2D,0x00,0x90,0xE8,0xA2,0x01,0xA1,0x22,0x04,0x05,0x2F,
+0x00,0x90,0xA3,0x22,0x04,0xA1,0x57,0x04,0x05,0x2D,0x00,0x90,0xA3,0x57,0x04,0xB9,
+0x50,0x00,0xBE,0x1E,0x04,0x8B,0x16,0x22,0x04,0xBB,0x04,0x00,0x90,0xE8,0x7D,0x01,
+0xBE,0x68,0x00,0x90,0x8D,0x1E,0xC1,0x04,0xE8,0x64,0x01,0xBE,0x51,0x04,0x8B,0x16,
+0x57,0x04,0xBB,0x06,0x00,0x90,0xE8,0x64,0x01,0xBE,0xBE,0x05,0x8D,0x1E,0x76,0x05,
+0xE8,0x53,0x01,0x73,0x28,0x0B,0xC0,0x75,0x0C,0xBE,0x1E,0x04,0xBB,0x04,0x00,0x90,
+0xE8,0x62,0x01,0xE2,0xBD,0xE8,0xF6,0x00,0xB8,0x40,0x00,0x8E,0xD8,0xC7,0x06,0x72,
+0x00,0x34,0x12,0xEA,0x00,0x00,0xFF,0xFF,0xB9,0x50,0x00,0xEB,0xBE,0x80,0x3E,0x84,
+0x05,0xFC,0x75,0xF4,0x81,0x3E,0x89,0x05,0x00,0x20,0x75,0xEC,0xA1,0xDC,0x04,0x39,
+0x06,0x91,0x05,0x74,0x02,0xEB,0x88,0x8B,0x16,0xDA,0x04,0x39,0x16,0x8F,0x05,0x74,
+0x03,0xE9,0x7B,0xFF,0x86,0xC4,0x86,0xD6,0x40,0x83,0xD2,0x00,0x86,0xC4,0x86,0xD6,
+0xA3,0xDC,0x04,0x89,0x16,0xDA,0x04,0xBE,0x51,0x04,0xBB,0x06,0x00,0x90,0xE8,0x04,
+0x01,0x8A,0x1E,0x9F,0x05,0xF6,0xC3,0x10,0x74,0x0D,0x53,0xBE,0x68,0x00,0x90,0x8D,
+0x1E,0xC1,0x04,0xE8,0xC9,0x00,0x5B,0xF6,0xC3,0x20,0x74,0x10,0xA1,0x99,0x05,0x86,
+0xE0,0xA3,0x34,0x0B,0xA1,0x97,0x05,0x86,0xE0,0xA3,0x36,0x0B,0x8B,0x0E,0xA0,0x05,
+0x86,0xCD,0x83,0xE9,0x04,0x0B,0xC9,0x74,0x37,0x33,0xD2,0xA1,0x34,0x0B,0xBF,0x10,
+0x00,0xF7,0xF7,0x8B,0xFA,0x03,0x06,0x36,0x0B,0x75,0x09,0x81,0xFA,0x00,0x0C,0x73,
+0x03,0xE9,0x51,0xFF,0x03,0xD1,0x89,0x16,0x34,0x0B,0xA3,0x36,0x0B,0x3D,0x00,0xA0,
+0x72,0x03,0xE9,0x40,0xFF,0x06,0x8E,0xC0,0x8D,0x36,0xA4,0x05,0xE8,0x5F,0x00,0x07,
+0xF6,0xC3,0x80,0x75,0x03,0xE9,0x40,0xFF,0xA1,0x38,0x0B,0x8B,0x0E,0x3A,0x0B,0xF6,
+0xC3,0x40,0x74,0x0B,0xA1,0x9D,0x05,0x86,0xC4,0x8B,0x0E,0x9B,0x05,0x86,0xCD,0x33,
+0xD2,0xBB,0x10,0x00,0xF7,0xF3,0x03,0xC1,0x50,0x52,0xE8,0x01,0x00,0xCB,0x06,0x1E,
+0x33,0xC0,0x8E,0xC0,0xB8,0x70,0x00,0x8E,0xD8,0xA1,0x02,0x00,0x26,0xA3,0x4C,0x00,
+0xA1,0x04,0x00,0x26,0xA3,0x4E,0x00,0xA1,0x06,0x00,0x26,0xA3,0x64,0x00,0xA1,0x08,
+0x00,0x26,0xA3,0x66,0x00,0x26,0xC6,0x06,0xD0,0x04,0x01,0x1F,0x07,0xC3,0xF7,0xC6,
+0x01,0x00,0x74,0x01,0xA4,0xD1,0xE9,0x9C,0xF3,0xA5,0x9D,0x73,0x01,0xA4,0xC3,0xB4,
+0xFE,0xB2,0x80,0xCD,0x13,0xC3,0xB4,0xFF,0xB2,0x80,0xCD,0x13,0xC3,0x50,0x51,0x53,
+0x8B,0xCB,0x32,0xFF,0xB4,0x02,0xCD,0x10,0xAC,0x33,0xDB,0xB4,0x0E,0xCD,0x10,0xE2,
+0xF7,0x5B,0x59,0x58,0xC3,0x50,0x51,0x8D,0x70,0xFF,0x8B,0xCB,0x8A,0x04,0x3C,0x39,
+0x7C,0x0A,0xB0,0x30,0x88,0x04,0x4E,0xE2,0xF3,0xEB,0x05,0x90,0xFE,0xC0,0x88,0x04,
+0x59,0x58,0xC3,0x53,0x65,0x61,0x72,0x63,0x68,0x69,0x6E,0x67,0x20,0x66,0x6F,0x72,
+0x20,0x52,0x50,0x4C,0x20,0x53,0x65,0x72,0x76,0x65,0x72,0x2C,0x20,0x52,0x65,0x74,
+0x72,0x69,0x65,0x73,0x20,0x3D,0x20,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x01,0x53,
+0x65,0x6E,0x64,0x69,0x6E,0x67,0x20,0x46,0x69,0x6C,0x65,0x20,0x52,0x65,0x71,0x75,
+0x65,0x73,0x74,0x73,0x20,0x74,0x6F,0x20,0x52,0x50,0x4C,0x20,0x53,0x65,0x72,0x76,
+0x65,0x72,0x2C,0x20,0x52,0x65,0x74,0x72,0x69,0x65,0x73,0x20,0x3D,0x20,0x30,0x30,
+0x30,0x30,0x00,0x03,0x57,0x61,0x69,0x74,0x69,0x6E,0x67,0x20,0x66,0x6F,0x72,0x20,
+0x44,0x61,0x74,0x61,0x20,0x46,0x72,0x61,0x6D,0x65,0x20,0x77,0x69,0x74,0x68,0x20,
+0x53,0x65,0x71,0x75,0x65,0x6E,0x63,0x65,0x20,0x4E,0x75,0x6D,0x62,0x65,0x72,0x3A,
+0x20,0x30,0x30,0x30,0x30,0x30,0x30,0x00,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x5A,0xFC,0xFC,0x03,0x00,0x57,0x00,0x01,0x00,0x08,
+0x40,0x03,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x08,0x00,0x06,0x40,0x09,0x05,0xBE,
+0x00,0x06,0x40,0x0A,0x00,0x01,0x00,0x0A,0x40,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x05,0x40,0x07,0xFC,0x00,0x2C,0x00,0x04,0x00,0x24,0xC0,0x05,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x40,
+0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5A,0xF8,
+0xFC,0x03,0x00,0x57,0x00,0x10,0x00,0x08,0x40,0x11,0x00,0x00,0x00,0x00,0x00,0x10,
+0x00,0x08,0x00,0x06,0x40,0x09,0x05,0xBE,0x00,0x06,0x40,0x0A,0x00,0x01,0x00,0x0A,
+0x40,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x40,0x07,0xFC,0x00,0x2C,0x00,
+0x04,0x00,0x24,0xC0,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x40,0x13};
+WORD sizeof_ripl_rom = sizeof( ripl_rom); // equal to 0x429
+
+BYTE Swap[256] =
+{
+0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,
+0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8,
+0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,
+0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc,
+0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa,
+0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6,
+0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,
+0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1,
+0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5,
+0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd,
+0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,
+0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb,
+0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff
+};
+
+void ReverseBits( PBYTE NodeAddress) {
+ DWORD i;
+ static CHAR Hex[] = "0123456789ABCDEF";
+ for ( i = 0; i < NODE_ADDRESS_LENGTH; i++) {
+ NodeAddress[ i] = Swap[ NodeAddress[ i] ];
+ PRINTF(( "%c", Hex[ NodeAddress[i]>>4]));
+ PRINTF(( "%c", Hex[ NodeAddress[i] & 0x0F]));
+ }
+ PRINTF(( "\n"));
+}
+
+HANDLE hCompletionEvent;
+LLC_CCB Ccb;
+PLLC_CCB pBadCcb;
+PLLC_CCB pCcb;
+
+LLC_DIR_OPEN_ADAPTER_PARMS DirOpenAdapterParms;
+LLC_ADAPTER_OPEN_PARMS AdapterOpenParms;
+LLC_EXTENDED_ADAPTER_PARMS ExtendedAdapterParms;
+LLC_DLC_PARMS DlcParms;
+
+LLC_BUFFER_CREATE_PARMS BufferCreate;
+PVOID pBuffer;
+DWORD cbBufferSize;
+
+LLC_DIR_STATUS_PARMS DirStatusParms;
+LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirectParms;
+LLC_RECEIVE_PARMS ReceiveParms;
+
+LLC_TRANSMIT_PARMS TransmitParms;
+
+BOOL EtherAcsLan( VOID)
+{
+ DWORD Status;
+
+ pCcb->hCompletionEvent = hCompletionEvent;
+ if ( !ResetEvent( pCcb->hCompletionEvent)) {
+ PRINTF(("EtherAcsLan: Reset(), Status=%d\n", GetLastError()));
+ return( FALSE);
+ }
+ Status = AcsLan( pCcb, &pBadCcb);
+ if ( Status != ACSLAN_STATUS_COMMAND_ACCEPTED) {
+ PRINTF(("EtherAcsLan: Acslan, Status=0x%x\n", Status));
+ return( FALSE);
+ }
+ Status = WaitForSingleObject( pCcb->hCompletionEvent, INFINITE);
+ PRINTF(("EtherAcsLan: DlcCommand=0x%x\n", Ccb.uchDlcCommand));
+ if ( Status != WAIT_OBJECT_0 || pCcb->uchDlcStatus) {
+ if ( Status == -1) {
+ Status = GetLastError();
+ }
+ PRINTF(("EtherAcsLan: Acslan, Status=0x%x, DlcStatus=0x%x\n", Status, pCcb->uchDlcStatus));
+ return( FALSE);
+ }
+
+ //
+ // Due to a DLC bug "pNext" field is trashed even on success
+ // code path. Until the bug is fix we must reinstate NULL.
+ //
+ pCcb->pNext = NULL;
+ return( TRUE);
+}
+
+BOOL RplDirCloseAdapter( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_CLOSE_ADAPTER;
+ return( EtherAcsLan());
+}
+
+BOOL RplDirOpenAdapter( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_OPEN_ADAPTER;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirOpenAdapterParms;
+ DirOpenAdapterParms.pAdapterParms = &AdapterOpenParms;
+ DirOpenAdapterParms.pExtendedParms = &ExtendedAdapterParms;
+ ExtendedAdapterParms.pSecurityDescriptor = NULL;
+ //
+ // With LLC_ETHERNET_TYPE_DEFAULT rpl server on build 392 did not
+ // detect an 802.3 client FIND frame.
+ //
+#define OLD_STUFF
+#ifdef OLD_STUFF
+ // Old stuff
+ ExtendedAdapterParms.LlcEthernetType = LLC_ETHERNET_TYPE_802_3;
+#else
+ // Antti's email
+ ExtendedAdapterParms.LlcEthernetType = LLC_ETHERNET_TYPE_DIX;
+#endif
+ ExtendedAdapterParms.hBufferPool = NULL; // for first open
+ DirOpenAdapterParms.pDlcParms = &DlcParms;
+ return( EtherAcsLan());
+}
+
+BOOL RplBufferCreate( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_BUFFER_CREATE;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&BufferCreate;
+ BufferCreate.pBuffer = pBuffer;
+ BufferCreate.cbBufferSize = cbBufferSize;
+ BufferCreate.cbMinimumSizeThreshold = 0x2000;
+ return( EtherAcsLan());
+}
+
+BOOL RplDirStatus( VOID) {
+ USHORT network_type;
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_STATUS;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirStatusParms;
+ if ( !EtherAcsLan()) {
+ return( FALSE);
+ }
+ network_type = DirStatusParms.usAdapterType;
+ if ( network_type != RPL_ADAPTER_ETHERNET) {
+ PRINTF(( "network_type=0x%x\n", network_type));
+ return( FALSE);
+ }
+ ReverseBits( DirStatusParms.auchNodeAddress);
+ return( TRUE);
+}
+
+BOOL RplDirOpenDirect( VOID) {
+ (VOID)memset((PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_DIR_OPEN_DIRECT;
+ Ccb.uchAdapterNumber = 0;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&DirOpenDirectParms;
+ memset((PCH)&DirOpenDirectParms, '\0', sizeof(DirOpenDirectParms));
+ DirOpenDirectParms.usOpenOptions = LLC_DIRECT_OPTIONS_ALL_MACS; // BUGBUG ??
+ DirOpenDirectParms.usEthernetType = 0x0600; // XNS identifier
+ DirOpenDirectParms.ulProtocolTypeMask = 0xFFFFFFFF;
+ DirOpenDirectParms.ulProtocolTypeMatch = 0x7200FFFF;
+ DirOpenDirectParms.usProtocolTypeOffset = 14; // where FFFF0072 of client begins
+ return( EtherAcsLan());
+}
+
+BOOL RplReceive( VOID) {
+ (VOID)memset( (PVOID)&Ccb, '\0', sizeof(Ccb));
+ (VOID)memset( (PVOID)&ReceiveParms, '\0', sizeof(ReceiveParms));
+ Ccb.uchDlcCommand = LLC_RECEIVE;
+ Ccb.u.pParameterTable = (PLLC_PARMS)&ReceiveParms;
+ ReceiveParms.usStationId = 0; // receive MAC & non-MAC frames
+ return( EtherAcsLan());
+}
+
+
+//
+// The following are XNS packet definitions used to crack EtherStart
+// packets.
+//
+
+#include <packon.h> // pack EtherStart structures
+
+struct sockaddr_ns {
+ DWORD net;
+ BYTE host[ NODE_ADDRESS_LENGTH];
+ WORD socket;
+};
+
+#define ETHERSTART_SOCKET 0x0104 // After swapping bytes
+
+struct _IDP {
+ WORD chksum;
+ WORD len;
+ BYTE xport_cntl;
+ BYTE packet_type;
+ struct sockaddr_ns dest;
+ struct sockaddr_ns src;
+};
+
+#define XNS_NO_CHKSUM 0xffff // XNS checksum
+#define PEX_TYPE 0x4 // packet exchange type
+
+struct _PEX {
+ DWORD pex_id;
+ WORD pex_client;
+};
+
+#define ETHERSERIES_CLIENT 0x0080 // After swapping bytes
+
+struct _ETHERSTART {
+ WORD ethershare_ver;
+ BYTE unit;
+ BYTE fill;
+ WORD block;
+ BYTE func;
+ BYTE error;
+ WORD bytes;
+};
+
+#define ETHERSHARE_VER 0
+#define FUNC_RESPONSE 0x80
+#define FUNC_READFILEREQ 0x20
+#define FUNC_READFILERESP (FUNC_READFILEREQ | FUNC_RESPONSE)
+
+typedef struct _ETHERSTART_REQ { // EtherStart Read File Request
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE filename[64];
+ WORD start;
+ WORD count;
+} ETHERSTART_REQ;
+
+typedef struct _ETHERSTART_RESP { // EtherStart Read File Response
+ struct _IDP idp;
+ struct _PEX pex;
+ struct _ETHERSTART es;
+ BYTE data[0x200];
+} ETHERSTART_RESP;
+
+
+PRCVBUF pRcvbuf;
+ETHERSTART_REQ * EtherstartReq;
+ETHERSTART_RESP EtherstartResp;
+//
+// When we use LAN_HEADER then destination address begins at offset
+// 14 of XmitData, while source address begins at offset 20.
+// However, DLC scrambles the data so that destination address begins
+// at offset 0 (instead of 2) - while source address still begins at
+// offset 6 - which is correct. LAN_HEADER_TOO is meant as yet another
+// DLC workaround.
+//
+typedef struct _LAN_HEADER_TOO {
+ BYTE dest_addr[ NODE_ADDRESS_LENGTH]; // Destination address
+ BYTE source_addr[ NODE_ADDRESS_LENGTH]; // Source address
+ BYTE routing_info_header[2]; // Routing information hdr
+} LAN_HEADER_TOO;
+
+struct {
+ XMIT_BUFFER XmitBuffer; // see comment for XMIT_BUFFER
+ LAN_HEADER_TOO LanHeader; // BUGBUG replaces LAN_HEADER
+} XmitQueue;
+
+#include <packoff.h> // restore default packing (done with EtherStart structures)
+
+VOID RplCopy( PVOID destination, PVOID source, DWORD length) {
+ memcpy( destination, source, length);
+}
+
+
+BOOL RplSend( VOID)
+{
+ WORD Temp;
+
+ (VOID)memset( (PVOID)&Ccb, '\0', sizeof(Ccb));
+ Ccb.uchDlcCommand = LLC_TRANSMIT_DIR_FRAME;
+
+ Ccb.u.pParameterTable = (PLLC_PARMS)&TransmitParms;
+ memset( (PVOID)&TransmitParms, '\0', sizeof(TransmitParms));
+ TransmitParms.uchXmitReadOption = DLC_DO_NOT_CHAIN_XMIT;
+
+ //
+ // Initialize XmitQueue which contains the first send buffer.
+ //
+ TransmitParms.pXmitQueue1 = (LLC_XMIT_BUFFER *)&XmitQueue;
+ memset( (PVOID)&XmitQueue, '\0', sizeof( XmitQueue.XmitBuffer));
+ XmitQueue.XmitBuffer.cbBuffer = sizeof( XmitQueue.LanHeader);
+ //
+ // In case of a direct open auchLanHeader[] returned by DLC contains
+ // LAN_HEADER structure without physical control fields. For now
+ // we work around this DLC bug by using offset 6 instead of 8 as a
+ // source address offset in auchLanHeader[].
+ //
+#define RPLDLC_SOURCE_OFFSET 6 // BUGBUG not 8 due to dlc bug
+ RplCopy( XmitQueue.LanHeader.dest_addr,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ RPLDLC_SOURCE_OFFSET],
+ NODE_ADDRESS_LENGTH);
+ RplCopy( XmitQueue.LanHeader.source_addr,
+ DirStatusParms.auchNodeAddress,
+ NODE_ADDRESS_LENGTH);
+ //
+ // These two are important for token ring already.
+ // I was hoping they will set XNS identifier field itself.
+ //
+ XmitQueue.LanHeader.routing_info_header[0] = 0x06;
+ XmitQueue.LanHeader.routing_info_header[1] = 0x00;
+
+ //
+ // Initialize EtherstartResp which contains the second & the last
+ // send buffer.
+ //
+ TransmitParms.pBuffer1 = (PVOID)&EtherstartResp;
+ Temp = min( EtherstartReq->count,
+ (WORD)(sizeof_ripl_rom - EtherstartReq->start));
+ TransmitParms.cbBuffer1 = (WORD)( sizeof(EtherstartResp) -
+ sizeof( EtherstartResp.data) + Temp);
+ EtherstartResp.idp.chksum = XNS_NO_CHKSUM;
+ EtherstartResp.idp.len = HILO( TransmitParms.cbBuffer1);
+ EtherstartResp.idp.packet_type = PEX_TYPE;
+ RplCopy( EtherstartResp.idp.dest.host,
+ (PBYTE)&pRcvbuf->b.auchLanHeader[ RPLDLC_SOURCE_OFFSET],
+ NODE_ADDRESS_LENGTH);
+ EtherstartResp.idp.dest.socket = ETHERSTART_SOCKET;
+ RplCopy( EtherstartResp.idp.src.host, DirStatusParms.auchNodeAddress,
+ NODE_ADDRESS_LENGTH);
+ EtherstartResp.idp.src.socket = ETHERSTART_SOCKET;
+ EtherstartResp.pex.pex_id = EtherstartReq->pex.pex_id;
+ EtherstartResp.pex.pex_client = ETHERSERIES_CLIENT;
+ EtherstartResp.es.func = FUNC_READFILERESP;
+ EtherstartResp.es.bytes = Temp; // stays intel ordered
+ RplCopy( EtherstartResp.data, &ripl_rom[ EtherstartReq->start], Temp);
+ return( EtherAcsLan());
+}
+
+PCHAR RG_EtherFileName[] = { // names of all legal boot request types
+ "BOOTPC.COM",
+ "BOOTSTAT.COM",
+ NULL
+ };
+int RG_EtherFileNameLength[] = { // strlen of the above strings
+ 10,
+ 12,
+ 0
+ };
+
+BOOL RplCrack( VOID)
+{
+ DWORD index;
+ PCHAR pFileName;
+
+ pRcvbuf = (PRCVBUF)ReceiveParms.pFirstBuffer;
+ EtherstartReq = (ETHERSTART_REQ *)&pRcvbuf->u;
+ //
+ // Make sure this request is for us.
+ //
+ if ( EtherstartReq->idp.packet_type != PEX_TYPE ||
+ EtherstartReq->pex.pex_client != ETHERSERIES_CLIENT ||
+ EtherstartReq->es.func != FUNC_READFILEREQ) {
+ return( FALSE); // this request is not for us
+ }
+
+ //
+ // Make sure this is a legal file request.
+ //
+ for ( index = 0, pFileName = RG_EtherFileName[ index];
+ pFileName != NULL;
+ pFileName = RG_EtherFileName[ ++index] ) {
+ if ( !memcmp( EtherstartReq->filename, pFileName,
+ RG_EtherFileNameLength[ index])) {
+ break;
+ }
+ }
+ if ( pFileName == NULL) {
+ return( FALSE); // this is not a legal file name request
+ }
+ return( TRUE);
+}
+
+BOOL Worker( VOID) {
+ if ( !RplBufferCreate()) {
+ return( FALSE);
+ }
+ if ( !RplDirStatus()) {
+ return( FALSE);
+ }
+ if ( !RplDirOpenDirect()) {
+ return( FALSE);
+ }
+ for ( ; ; ) {
+ if ( !RplReceive()) {
+ return( FALSE);
+ }
+ if ( !RplCrack()) {
+ return( FALSE);
+ }
+ if ( !RplSend()) {
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+BOOL _CRTAPI1 main( void) {
+ BOOL success;
+ pCcb = &Ccb;
+ hCompletionEvent = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ TRUE, // initial state is signalled
+ NULL // no name
+ );
+ if ( hCompletionEvent == NULL) {
+ PRINTF(( "CreateEvent() error\n", GetLastError()));
+ return( FALSE);
+ }
+ cbBufferSize = 0xA000;
+ pBuffer = GlobalAlloc( GMEM_FIXED, cbBufferSize);
+ if ( pBuffer == NULL) {
+ PRINTF(( "GlobalAlloc() error\n", GetLastError()));
+ return( FALSE);
+ }
+ if ( !RplDirOpenAdapter()) {
+ return( FALSE);
+ }
+ success = Worker();
+ RplDirCloseAdapter();
+ return( success);
+}
+
+
diff --git a/private/net/svcdlls/rpl/doc/lm21ddk.doc b/private/net/svcdlls/rpl/doc/lm21ddk.doc
new file mode 100644
index 000000000..1a7bb04e4
--- /dev/null
+++ b/private/net/svcdlls/rpl/doc/lm21ddk.doc
Binary files differ
diff --git a/private/net/svcdlls/rpl/doc/rpl.doc b/private/net/svcdlls/rpl/doc/rpl.doc
new file mode 100644
index 000000000..cb3be7592
--- /dev/null
+++ b/private/net/svcdlls/rpl/doc/rpl.doc
Binary files differ
diff --git a/private/net/svcdlls/rpl/exe/makefile b/private/net/svcdlls/rpl/exe/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/exe/rplsvc.c b/private/net/svcdlls/rpl/exe/rplsvc.c
new file mode 100644
index 000000000..953cd8a8e
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/rplsvc.c
@@ -0,0 +1,146 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.c
+
+Abstract:
+
+ This is the main routine for the Remote Program Load Service.
+
+Notes:
+
+ BUGBUG For now rplsvc.exe is built in ..\server directory
+ since building it in ..\server is more convenient.
+ The code here may be out of date compared to ..\server\rplsvc.c.
+
+ You must modify ..\server to build rplsvc.lib (instead of rplsvc.exe)
+ in order for this code to link.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - Februrary - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 10-Feb-1993 vladimv
+ created
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winsvc.h> // Service control APIs
+
+#include <lmcons.h>
+#include <lmerr.h> // NERR_ and ERROR_ equates.
+#include <lmsname.h>
+
+#include <rpcutil.h> // NetpInitRpcServer()
+#include <netdebug.h> // NetpKdPrint(())
+#include <secobj.h> // NetpCreateWellKnownSids()
+
+
+//
+// Function Prototypes.
+//
+
+VOID
+RPL_main(
+ DWORD argc,
+ LPWSTR * argv
+ );
+
+//
+// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
+//
+// Add new service entries here.
+//
+
+SERVICE_TABLE_ENTRY RPLServiceDispatchTable[] = {
+ { SERVICE_RIPL, RPL_main },
+ { NULL, NULL }
+};
+
+
+
+VOID _CRTAPI1
+main (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is a main routine for the LANMan Remote Program Load Services.
+
+ It basically sets up the ControlDispatcher and, on return, exits from
+ this main thread. The call to NetServiceStartCtrlDispatcher does
+ not return until all services have terminated, and this process can
+ go away.
+
+ It will be up to the ControlDispatcher thread to start/stop/pause/continue
+ any services. If a service is to be started, it will create a thread
+ and then call the main routine of that service.
+
+
+Arguments:
+
+ Anything passed in from the "command line". Currently, NOTHING.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ //
+ // Create well-known SIDs
+ //
+ if (! NT_SUCCESS (ntstatus = NetpCreateWellKnownSids(NULL))) {
+ NetpKdPrint(("[RplSvc] Failed to create well-known SIDs %08lx\n",
+ ntstatus));
+ return;
+ }
+
+
+ //
+ // Initialize the RpcServer Locks.
+ //
+
+ NetpInitRpcServer();
+
+ //
+ // Call StartServiceCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+
+ if (! StartServiceCtrlDispatcher ( RPLServiceDispatchTable)) {
+ //
+ // BUGBUG: Log an event for failing to start control dispatcher
+ //
+ NetpKdPrint(("[RplSvc] Failed to start control dispatcher %lu\n",
+ GetLastError()));
+ }
+
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/rpl/exe/rplsvc.rc b/private/net/svcdlls/rpl/exe/rplsvc.rc
new file mode 100644
index 000000000..b2fc29e3c
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/rplsvc.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Net Remote Program Load Service App"
+#define VER_INTERNALNAME_STR "atrpl.exe"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/rpl/exe/sources b/private/net/svcdlls/rpl/exe/sources
new file mode 100644
index 000000000..eaac46918
--- /dev/null
+++ b/private/net/svcdlls/rpl/exe/sources
@@ -0,0 +1,97 @@
+!IF 0
+
+Copyright (c) 1989-92 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+!ENDIF
+
+#
+# The TARGETNAME variable is defined by the developer. It is the name of
+# the target (component) that is being built by this makefile. It
+# should NOT include any path or file extension information.
+#
+
+MAJORCOMP =net
+MINORCOMP =rplsvc
+TARGETNAME=rplsvc
+
+#
+# The TARGETPATH and TARGETTYPE varialbes are defined by the developer.
+# The first specifies where the target is to be build. The second specifies
+# the type of target (either PROGRAM, DYNLINK or LIBRARY)
+#
+
+TARGETPATH=obj
+
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\server\obj\*\rplsvc.lib \
+ ..\dll\obj\*\rplnet.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib
+
+#
+# The INCLUDES variable specifies any include paths that are specific to
+# this source directory. Separate multiple directory paths with single
+# semicolons. Relative path specifications are okay.
+#
+
+INCLUDES=..\..\..\inc;..\..\..\..\inc;$(BASEDIR)\public\sdk\inc
+
+#
+# The SOURCES variable is defined by the developer. It is a list of all the
+# source files for this component. Each source file should be on a separate
+# line using the line continuation character. This will minimize merge
+# conflicts if two developers adding source files to the same component.
+#
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+#
+# BUGBUG Messages are not bound properly with RplSvc.exe if it
+# BUGBUG built from within the exe directory.
+#
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= rplsvc.c rplsvc.rc
+
+UMTYPE=console
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+#
+# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to
+# include .\makefile.inc immediately after it specifies the top
+# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF
+# also expands the value of the NTTARGETFILES variable at the end of the
+# list of dependencies for the all target. Useful for specifying additional
+# targets and dependencies that don't fit the general case covered by
+# MAKEFILE.DEF
+#
diff --git a/private/net/svcdlls/rpl/imports.idl b/private/net/svcdlls/rpl/imports.idl
new file mode 100644
index 000000000..3cca04e23
--- /dev/null
+++ b/private/net/svcdlls/rpl/imports.idl
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+ 03-Apr-1991 danl
+ created
+
+--*/
+
+[
+ uuid(12345678-1234-ABCD-EF00-9948756789AB),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ version(1.0)
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+//
+// All .idl files need to contain at least one function prototype
+//
+
+DWORD Dummy( [in] DWORD DummyParm);
+}
diff --git a/private/net/svcdlls/rpl/inc/adapter.h b/private/net/svcdlls/rpl/inc/adapter.h
new file mode 100644
index 000000000..744173cad
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/adapter.h
@@ -0,0 +1,52 @@
+/*++
+
+Module Name:
+
+ adapter.h
+
+Abstract:
+
+ Describes layout of JET database table used for ADAPTER structures.
+
+--*/
+
+#ifdef RPLADAPTER_ALLOCATE
+#define EXTERN_ADAPTER
+#define INIT_ADAPTER( _x) = _x
+#else
+#define EXTERN_ADAPTER extern
+#define INIT_ADAPTER( _x)
+#endif
+
+//
+// Indices of entries in AdapterTable[] - server column array.
+//
+#define ADAPTER_AdapterName 0
+#define ADAPTER_AdapterComment 1
+#define ADAPTER_Flags 2
+#define ADAPTER_TABLE_LENGTH 3
+
+RPL_COLUMN_INFO AdapterTable[]
+#ifdef RPLADAPTER_ALLOCATE
+ = {
+ { "AdapterName", JET_coltypBinary, 0}, // address of unknown rpl client
+ { "AdapterComment", JET_coltypBinary, 0}, // default comment
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLADAPTER_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLADAPTER_ALLOCATE is not defined
+//#define ADAPTER_TABLE_LENGTH (sizeof(AdapterTable)/sizeof(AdapterTable[0]))
+//
+
+#define ADAPTER_INDEX_AdapterName "foo" // + AdapterName
+
+#define ADAPTER_TABLE_NAME "Adapter"
+#define ADAPTER_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define ADAPTER_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_ADAPTER JET_TABLEID AdapterTableId;
+#endif // RPL_RPLCNV
diff --git a/private/net/svcdlls/rpl/inc/boot.h b/private/net/svcdlls/rpl/inc/boot.h
new file mode 100644
index 000000000..2abb4babb
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/boot.h
@@ -0,0 +1,59 @@
+/*++
+
+Module Name:
+
+ boot.h
+
+Abstract:
+
+ Describes layout of JET database table used for BOOT structures.
+
+--*/
+
+#ifdef RPLBOOT_ALLOCATE
+#define EXTERN_BOOT
+#define INIT_BOOT( _x) = _x
+#else
+#define EXTERN_BOOT extern
+#define INIT_BOOT( _x)
+#endif
+
+//
+// Indices of entries in BootTable[] - server column array.
+//
+#define BOOT_BootName 0
+#define BOOT_BootComment 1
+#define BOOT_Flags 2
+#define BOOT_VendorName 3
+#define BOOT_BbcFile 4
+#define BOOT_WindowSize 5
+#define BOOT_VendorId 6
+#define BOOT_TABLE_LENGTH 7
+
+RPL_COLUMN_INFO BootTable[]
+#ifdef RPLBOOT_ALLOCATE
+ = {
+ { "BootName", JET_coltypBinary, 0}, // id of boot block record
+ { "BootComment", JET_coltypBinary, 0}, // comment for boot block record
+ { "Flags", JET_coltypLong, 0},
+ { "VendorName", JET_coltypBinary, 0}, // VendorId as hex unicode string
+ { "BbcFile", JET_coltypBinary, 0}, // BBC file name
+ { "WindowSize", JET_coltypLong, 0}, // used with acknowledgments
+ { "VendorId", JET_coltypLong, 0} // common adapter id digits
+}
+#endif // RPLBOOT_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLBOOT_ALLOCATE is not defined
+//#define BOOT_TABLE_LENGTH (sizeof(BootTable)/sizeof(BootTable[0]))
+//
+
+#define BOOT_INDEX_VendorIdBootName "foo" // + VendorId + BootName
+#define BOOT_INDEX_BootName "goo" // + BootName
+
+#define BOOT_TABLE_NAME "Boot"
+#define BOOT_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define BOOT_TABLE_DENSITY 100 // initial density
+
+EXTERN_BOOT JET_TABLEID BootTableId;
diff --git a/private/net/svcdlls/rpl/inc/config.h b/private/net/svcdlls/rpl/inc/config.h
new file mode 100644
index 000000000..6cb135433
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/config.h
@@ -0,0 +1,95 @@
+/*++
+
+Module Name:
+
+ config.h
+
+Abstract:
+
+ Describes layout of JET database table used for CONFIG structures.
+
+--*/
+
+#ifdef RPLCONFIG_ALLOCATE
+#define EXTERN_CONFIG
+#define INIT_CONFIG( _x) = _x
+#else
+#define EXTERN_CONFIG extern
+#define INIT_CONFIG( _x)
+#endif
+
+#define CONFIG_ConfigName 0
+#define CONFIG_ConfigComment 1
+#define CONFIG_Flags 2
+#define CONFIG_BootName 3
+#define CONFIG_DirName 4
+#define CONFIG_DirName2 5
+#define CONFIG_DirName3 6
+#define CONFIG_DirName4 7
+#define CONFIG_FitShared 8
+#define CONFIG_FitPersonal 9
+#define CONFIG_TABLE_LENGTH 10
+
+EXTERN_CONFIG RPL_COLUMN_INFO ConfigTable[ CONFIG_TABLE_LENGTH]
+#ifdef RPLCONFIG_ALLOCATE
+ = {
+ { "ConfigName", JET_coltypBinary, 0},
+ { "ConfigComment", JET_coltypBinary, 0},
+ { "Flags", JET_coltypLong, 0},
+ { "BootName", JET_coltypBinary, 0},
+ { "DirName", JET_coltypBinary, 0},
+ { "DirName2", JET_coltypBinary, 0},
+ { "DirName3", JET_coltypBinary, 0},
+ { "DirName4", JET_coltypBinary, 0},
+ { "FitShared", JET_coltypBinary, 0},
+ { "FitPersonal", JET_coltypBinary, 0}
+}
+#endif // RPLCONFIG_ALLOCATE
+;
+
+//
+// The block below is needed by RPLCNV.EXE only. We include it here
+// because it is essential that order of elements and lengths of
+// ConfigTable and ConfigParseTable arrays are kept in sync.
+//
+#ifdef RPL_RPLCNV
+typedef struct _CONFIG_PARSE_INFO {
+ PWCHAR Name; // RPLMGR.INI name - NULL for new entries
+ PVOID Value; // RPLMGR.INI value
+ DWORD Size; // size in bytes of the above value
+} CONFIG_PARSE_INFO, *PCONFIG_PARSE_INFO;
+EXTERN_CONFIG CONFIG_PARSE_INFO ConfigParseTable[ CONFIG_TABLE_LENGTH]
+#ifdef RPLCONFIG_ALLOCATE
+ = {
+ { L"name", NULL, 0},
+ { L"comment", NULL, 0},
+ { NULL, NULL, 0}, // a placeholder for Flags fields
+ { L"bblink", NULL, 0},
+ { L"dirname", NULL, 0},
+ { L"dirname2", NULL, 0},
+ { L"dirname3", NULL, 0},
+ { L"dirname4", NULL, 0},
+ { L"fitfileshared", NULL, 0},
+ { L"fitfilepersonal", NULL, 0}
+}
+#endif // RPLCONFIG_ALLOCATE
+;
+#endif // RPL_RPLCNV
+
+
+//
+// This definition gives wrong result when RPLCONFIG_ALLOCATE is not defined
+//#define CONFIG_TABLE_LENGTH (sizeof(ConfigTable)/sizeof(ConfigTable[0]))
+//
+
+#define CONFIG_INDEX_ConfigName "foo" // +ConfigName
+#define CONFIG_INDEX_BootNameConfigName "goo" // +BootName+ConfigName
+
+#define CONFIG_TABLE_NAME "Config"
+#define CONFIG_TABLE_PAGE_COUNT 10 // initial number of 4K pages
+#define CONFIG_TABLE_DENSITY 80 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_CONFIG JET_TABLEID ConfigTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/i_lmrpl.h b/private/net/svcdlls/rpl/inc/i_lmrpl.h
new file mode 100644
index 000000000..1cbde89b6
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/i_lmrpl.h
@@ -0,0 +1,26 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ i_lmrpl.h
+
+Abstract:
+
+ This file desribes hidden structures used with RPL service apis.
+ It is a private complement of lmrpl.h public file.
+
+ Currently structures defined in this file are mostly used as
+ placeholders for future extensions, and to suppress MIDL warnings
+ and to make sure code is already set to support different levels.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 18-Nov-1993
+
+--*/
+
+typedef struct _RPL_INFO_1 {
+ DWORD AdapterPolicy; // bitmask
+} RPL_INFO_1, *PRPL_INFO_1, *LPRPL_INFO_1;
diff --git a/private/net/svcdlls/rpl/inc/imports.h b/private/net/svcdlls/rpl/inc/imports.h
new file mode 100644
index 000000000..5b2991ab0
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/imports.h
@@ -0,0 +1,45 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imports.h
+
+Abstract:
+
+ This file allows us to include standard system header files in the
+ .idl file. The main .idl file imports a file called import.idl.
+ This allows the .idl file to use the types defined in these header
+ files. It also causes the following line to be added in the
+ MIDL generated header file:
+
+ #include "imports.h"
+
+ Thus these types are available to the RPC stub routines as well.
+
+Author:
+
+ Dan Lafferty (danl) 07-May-1991
+
+Revision History:
+
+
+--*/
+
+
+#include <windef.h>
+#include <lmcons.h>
+
+#ifdef MIDL_PASS
+#ifdef UNICODE
+#define LPTSTR [string] wchar_t*
+#else
+#define LPTSTR [string] LPTSTR
+#endif
+#define LPSTR [string] LPSTR
+#define BOOL DWORD
+#endif
+
+#include <lmrpl.h> // public definitions
+#include "i_lmrpl.h" // internal definitions
diff --git a/private/net/svcdlls/rpl/inc/profile.h b/private/net/svcdlls/rpl/inc/profile.h
new file mode 100644
index 000000000..a012ec86c
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/profile.h
@@ -0,0 +1,62 @@
+/*++
+
+Module Name:
+
+ profile.h
+
+Abstract:
+
+ Describes layout of JET database table used for PROFILE structures.
+
+--*/
+
+#ifdef RPLPROFILE_ALLOCATE
+#define EXTERN_PROFILE
+#define INIT_PROFILE( _x) = _x
+#else
+#define EXTERN_PROFILE extern
+#define INIT_PROFILE( _x)
+#endif
+
+//
+// Indices of entries in ProfileTable[] - profile column array.
+//
+#define PROFILE_ProfileName 0
+#define PROFILE_ProfileComment 1
+#define PROFILE_ConfigName 2
+#define PROFILE_BootName 3
+#define PROFILE_FitShared 4
+#define PROFILE_FitPersonal 5
+#define PROFILE_Flags 6
+#define PROFILE_TABLE_LENGTH 7
+
+EXTERN_PROFILE RPL_COLUMN_INFO ProfileTable[ PROFILE_TABLE_LENGTH]
+#ifdef RPLPROFILE_ALLOCATE
+ = {
+ { "ProfileName", JET_coltypBinary, 0}, // profile name
+ { "ProfileComment", JET_coltypBinary, 0}, // profile comment
+ { "ConfigName", JET_coltypBinary, 0}, // configuration name
+ { "BootName", JET_coltypBinary, 0}, // boot block id
+ { "FitShared", JET_coltypBinary, 0}, // fit file for wksta sharing
+ { "FitPersonal", JET_coltypBinary, 0}, // fit file for wksta exclusive
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLPROFILE_ALLOCATE
+;
+//
+// This definition gives wrong result when RPLPROFILE_ALLOCATE is not defined
+//#define PROFILE_TABLE_LENGTH (sizeof(ProfileTable)/sizeof(ProfileTable[0]))
+//
+
+#define PROFILE_INDEX_ProfileName "foo" // +ProfileName
+#define PROFILE_INDEX_BootNameProfileName "goo" // +BootName+ProfileName
+#define PROFILE_INDEX_ConfigNameProfileName "hoo" // +ConfigName+ProfileName
+
+#define PROFILE_TABLE_NAME "Profile"
+#define PROFILE_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define PROFILE_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_PROFILE JET_TABLEID ProfileTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/riplcons.h b/private/net/svcdlls/rpl/inc/riplcons.h
new file mode 100644
index 000000000..cfa6674cf
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/riplcons.h
@@ -0,0 +1,130 @@
+/*++
+
+Module Name:
+
+ riplcons.h
+
+Abstract:
+
+ RPL constants
+
+--*/
+
+#define RPL_SUCCESS NO_ERROR // defined to be ZERO (LONG).
+
+#define INVALID_FILE_OFFSET ((DWORD)(-1L)) // for use with SetFilePointer()
+
+#define BITSET( i, mask) ( (mask & (1<<i)) != 0)
+
+
+//
+// Bits and values of type file in file map
+// Bits 0 - 3 are reserved for the types of data files and
+// special cases. Bits 4 - 7 are boolean bits of executable and sys
+// files. When bits 0 - 3 are used, bits 4 - 7 bust be reset.
+
+// IS_EXE_SYS bit is set for exe-s and device drivers that have exe-format
+
+#define IS_BIN_SYS 0x0000
+#define IS_EXE_SYS 0x0100 // set if loadable file has exe-format
+#define IS_COM_FILE 0x0200
+#define IS_EXE_FILE 0x0300
+#define IS_MOVEABLE 0x0400
+#define EXEC_IN_LOW_MEM 0x0800
+#define EXEC_IN_FREE_MEM 0x1000
+#define OTHER_FILES_MASK 0x00FF
+#define BINARY_LOADER 15
+#define DATA_FILE 14
+#define BINARY_LOADER_DATA 13
+#define LOADER_PARAM 12
+#define RPL_BOOT_TYPE 11
+#define IS_EXE_FORMAT 0x0100
+#define IS_EXECUTABLE_FILE 0x0200
+
+#define DRV_TYPE 0x0000
+#define EXE_TYPE 0x0200
+#define ORG_TYPE 1
+#define BASE_TYPE 6
+#define UNKNOWN_CONFIG_TYPE 7
+
+//
+// work station types of returned in parameter wksta_type
+//
+
+#define PCDOS_WKSTA 0
+#define OS2_WKSTA 1
+
+//
+// The patched offsets of RPLBOOT.SYS
+//
+
+#define BBLOCK_BASE_INDEX 8
+#define MAX_NLS_NUM 5
+#define NLS_BASE_INDEX 10
+#define NLS_INCREMENT 8
+#define NO_CHKSUM_USED 1963
+
+//
+// Misc conts
+//
+
+#define LM20_WKSTA_LINE 257 // max rpl.map wksta line len supported by rplmfsd.sys (of lm 2.0?)
+
+
+//
+// these constants are used only rplservr, not in rplboot.asm, sorry
+//
+
+#define OFFSET_RPLBOOT_HDR 16
+#define NLS_VERSION_10 1 // 1st version number of NLS
+#define BBVERSION_10 3 // Boot block format V2.1(Free type)
+
+
+//
+// Parameter substitution strings for TCPIP protocol.ini parameters
+// used by RPLBOOT.SYS
+//
+
+#define TEMPLATESIZE 15
+#define TCPIP_PREFIX "(TCPIP"
+#define TCPIP_ADDRESS "(TCPIP_ADDRESS)"
+#define TCPIP_SUBMASK "(TCPIP_SUBMASK)"
+#define TCPIP_GATEWAY "(TCPIP_GATEWAY)"
+
+#define TILDE_CHAR L'~'
+#define SPACE_CHAR L' '
+#define COMMENT_CHAR L';'
+#define LINE_FEED_CHAR L'\n' // 0xA or \012
+#define NEW_LINE_CHAR LINE_FEED_CHAR
+
+#define MAXWORD ((WORD)-1) // it is not safe to compare WORDs with -1
+
+#define END_OF_TEXT_FILE_CHAR ((WCHAR)0x1a) // ctrl-Z == 'Z' - 0x40
+
+#define RPL_MAX_PROFILE_NAME_SIZE ((RPL_MAX_PROFILE_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_WKSTA_NAME_SIZE ((RPL_MAX_WKSTA_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_CONFIG_NAME_SIZE ((RPL_MAX_CONFIG_NAME_LENGTH + 1) * sizeof(WCHAR))
+#define RPL_MAX_BOOT_NAME_SIZE ((RPL_MAX_BOOT_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+#define RPL_MAX_ADAPTER_NAME_LENGTH RPL_ADAPTER_NAME_LENGTH
+#define RPL_MAX_ADAPTER_NAME_SIZE ((RPL_MAX_ADAPTER_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+#define RPL_MAX_VENDOR_NAME_LENGTH RPL_VENDOR_NAME_LENGTH
+#define RPL_MAX_VENDOR_NAME_SIZE ((RPL_MAX_VENDOR_NAME_LENGTH + 1) * sizeof(WCHAR))
+
+
+
+//
+// RPL_EVENTLOG_NAME is the name of registry key (under EventLog service)
+// used to interpret events for RPL service.
+//
+
+#define RPL_EVENTLOG_NAME TEXT( "RemoteBoot")
+
+
+//
+// Define this flag to move to JET500
+// Note that the "sources" files must be updated seperately to refer to
+// JET.LIB or JET500.LIB.
+//
+#define __JET500 1
diff --git a/private/net/svcdlls/rpl/inc/ripltyps.h b/private/net/svcdlls/rpl/inc/ripltyps.h
new file mode 100644
index 000000000..bd62bf9e9
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/ripltyps.h
@@ -0,0 +1,137 @@
+/*++
+
+Module Name: ripltyps.h
+
+--*/
+
+struct _PARAGRAPH {
+ union {
+ DWORD dwords[4];
+ WORD words[8];
+ BYTE bytes[16];
+ } t;
+} PARAGRAPH, *PPARAGRAPH;
+
+//****************************************************************
+//
+// The boot block structure is totally redefined in LM 2.1, because
+// the extending of the LM 2.0 generated too complicated code.
+//
+// All these data structures are in the boot block header. Thus
+// they are common for RPLSERVR and RPLBOOT. The boot data is the
+// parameter block of RPL MiniFSD and the resource table is for
+// OS/2 1.2 Loader. There must be something other for OS/2 2.0 because
+// paragraph addresses can't be used (kernel > 640 kB).
+//
+// Boot block is a file list including a header. There
+// are also the standard dos parameters and some special RIPL data
+// stuctures in the header.
+//
+// The boot block root is actually the header of RPLBOOT.SYS file
+// (that can be anywhere in the boot block).
+// Remote IPL ROM is requested to jump to there. RPLBOOT finds
+// the boot block base address in its header (patched by RPLSERVR).
+// All pointers (except the file 32 bit physical pointers) in boot
+// block headers are offsets relative to the boot block base address.
+//
+//****************************************************************
+
+// data structure used in RPLBOOT offset 16,
+typedef struct rplboot_header {
+ BYTE achIdStamp[3]; // "RPL" string for identification
+ BYTE bNul; // terminator
+ BYTE bBbVersion; // version number of boot block format
+ BYTE bNlsVersion; // version number of NLS patches (1 = make)
+ WORD usNlsPatchOff; // offset from file start of NLS patch files
+ DWORD phBootBlockHeader; // 32 bits physical address
+} RPLBOOT_HEADER, *PRPLBOOT_HEADER;
+
+//
+// The segment of parameter and name pointers is the segment of
+// file list table. The parameter block of executable files
+// will be copied to the PSP offset 80H (Program Segment Prefix).
+// The handle I/O and the error messages of rplboot requires
+// file names. The check sum is not calculated, if the chk_sum
+// field is 1963 ('magic number').
+//
+
+//
+// It may not be required to pack structures below, but for testing
+// purposes it is useful since this should give us 1-1 correspondence
+// with OS/2 rpl server.
+//
+// FILE_DATA packed size is 0x16 => unpacked is 0x18
+// BOOT_BLOCK_HDR packed size is 0x12 => unpacked is 0x14
+//
+#include <packon.h> // pack structures in boot block
+
+typedef struct _FILE_DATA {
+ DWORD file_addr; // 32 bit physical address of file
+ DWORD file_len; // file length (bytes)
+ DWORD extra_mem; // extra memory (bytes)
+ WORD name_offset; // offset of name string
+ WORD param_offset; // offset of parameter block
+ WORD chk_sum; // 16 bit add checksum of the file
+ WORD file_type; // type of file
+ BYTE param_len; // length of params block
+ BYTE pad1; // pad this to next even byte
+} FILE_DATA, *PFILE_DATA;
+
+// new boot block header structure used in LAN Manager 2.1 (and later)
+// the offsets are relative from the boot block base address
+
+typedef struct _BOOT_BLOCK_HDR {
+ WORD id_stamp; // if 0x18cd (int 18H) => extended structure
+ WORD usInfoLevel; // info level of this structure
+ WORD cbSize; // size of boot block header struct (and data)
+ WORD file_list_len; // length of file list table
+ WORD offRplWkstaLine; // offset ASCIZ rpl.map wksta line (any length)
+ WORD offResourceTbl; // pointer of IFS resource table
+ WORD cbResourceTbl; // size of resource table struct
+ WORD offBootData; // offset of LM 2.0 level boot structure
+ WORD offMBootData; // offset of the buffer used in the multi boot
+ FILE_DATA aFileData[1]; // dynamic array for file data
+} BOOT_BLOCK_HDR, *PBOOT_BLOCK_HDR;
+
+// DON't MODIFY, the structures fixed in 1.2 LDR spec
+
+typedef struct _RESOURCE {
+ WORD pos_in_paras; // paragraph of the file in PC memory
+ DWORD file_len; // length of file in bytes
+} RESOURCE;
+
+typedef struct _RESOURCE_TABLE {
+ WORD entries; // number of entries 4 (or 3 if no BootData)
+ RESOURCE file_tbl[4];
+} RESOURCE_TBL, *PRESOURCE_TBL;
+
+
+//
+// BootData structure is fixed in RPL MFSD,
+// negotiate with Randy before any changes
+// All offsets are relative from the beginning of the structure.
+//
+
+typedef struct _BOOT_DATA {
+ WORD cbSize; // size of the whole structure
+ DWORD int_5c; // netbios interrupt saved by rplboot.sys
+ WORD cbWkstaLine; // size of wksta line
+ WORD offWkstaLine; // offset of ASCIIZ wksta line
+ WORD cbFit; // size of the FIT file
+ WORD offFit; // offset of ASCIIZ FIT file
+ WORD cbCodePage; // size of NLS Code Page
+ WORD offCodePage; // offset of Code Page Table Dynamic data area for the buffers
+} BOOT_DATA, *PBOOT_DATA;
+
+//
+// Defines the structure of the element in a variable len multiboot
+// info table
+//
+typedef struct _MBOOTDATA {
+ WORD cbSize; // size of the dynamic structure
+ BYTE achProfile[16]; // profile name
+ BYTE achProfileComment[1]; // variable len comment
+} MBOOTDATA, *PMBOOTDATA;
+
+#include <packoff.h> // restore default packing (done with boot block)
+
diff --git a/private/net/svcdlls/rpl/inc/rpldb.h b/private/net/svcdlls/rpl/inc/rpldb.h
new file mode 100644
index 000000000..3990ced31
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldb.h
@@ -0,0 +1,30 @@
+/*++
+
+Module Name:
+
+ rpldb.h
+
+Abstract:
+
+ Main include file for JET database portion of RPL service & RPL convert
+ program.
+
+
+ Auxiliary include files (adapter.h , boot.h, config.h, profile.h &
+ wksta.h) are JET table specific.
+
+--*/
+
+#define RPL_SERVICE_DATABASE "rplsvc.mdb"
+#define RPL_SERVICE_DATABASE_W L"rplsvc.mdb"
+#define RPL_SYSTEM_DATABASE "system.mdb"
+#define RPL_SYSTEM_DATABASE_W L"system.mdb"
+#define RPL_TEMP_DATABASE "temp.mdb"
+#define RPL_TEMP_DATABASE_W L"temp.mdb"
+
+typedef struct _RPL_COLUMN_INFO {
+ CHAR * ColumnName;
+ JET_COLTYP ColumnType;
+ JET_COLUMNID ColumnId;
+} RPL_COLUMN_INFO, *PRPL_COLUMN_INFO;
+
diff --git a/private/net/svcdlls/rpl/inc/rpldebug.h b/private/net/svcdlls/rpl/inc/rpldebug.h
new file mode 100644
index 000000000..d1a0a6ec7
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldebug.h
@@ -0,0 +1,88 @@
+/*++
+
+Module:
+
+ rpldebug.h
+
+Abstract:
+
+ Definitions of debug data structures.
+
+--*/
+
+#if DBG
+#define RPL_DEBUG
+#endif // DBG
+
+#define NODE_ADDRESS_LENGTH 6
+
+#ifdef RPL_DEBUG
+
+extern int RG_DebugLevel; // needed by other modules
+extern int RG_Assert; // needed by other modules
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...);
+
+#define RplDump( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ { \
+ if ( DBG_CONDITION) { \
+ if ( RG_Assert!= 0) { \
+ RplDebugPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ } \
+ RplDebugPrint DBG_PRINT_ARGUMENTS; \
+ } \
+ }
+
+#define RPL_RETURN( arg) RplDump(++RG_Assert,("File %s, Line %d\n", __FILE__, __LINE__)); return( arg)
+
+//
+// We do not define
+//
+// #define RPL_ASSERT( condition) ASSERT( condition)
+//
+// because we want to break even on a free build and in user mode
+// (when rplsvc runs under ntsd).
+//
+#define RPL_ASSERT( condition) \
+ { \
+ if ( !( condition)) { \
+ ++RG_Assert; \
+ RplDebugPrint( "File %s, Line %d\n", __FILE__, __LINE__); \
+ } \
+ }
+
+#define RPL_DEBUG_MEMALLOC ((DWORD)0x00000001) // memory allocations
+#define RPL_DEBUG_INIT ((DWORD)0x00000002)
+#define RPL_DEBUG_FDR ((DWORD)0x00000004) // file data response
+#define RPL_DEBUG_FIND ((DWORD)0x00000008) // find
+#define RPL_DEBUG_FOUND ((DWORD)0x00000010) // found
+#define RPL_DEBUG_SFR ((DWORD)0x00000020) // send file request
+#define RPL_DEBUG_MISC ((DWORD)0x00000040) // miscelaneous
+#define RPL_DEBUG_REQUEST ((DWORD)0x00000080) // request thread
+#define RPL_DEBUG_SERVICE ((DWORD)0x00000100)
+#define RPL_DEBUG_WORKER ((DWORD)0x00000200) // worker thread
+#define RPL_DEBUG_MEMORY ((DWORD)0x00000400) // other memory
+#define RPL_DEBUG_FLOW ((DWORD)0x00000800) // other flow
+#define RPL_DEBUG_FLOWINIT ((DWORD)0x00001000) // init flow
+#define RPL_DEBUG_ETHER ((DWORD)0x00002000) // EtherStart
+#define RPL_DEBUG_DB ((DWORD)0x00004000) // misc database
+#define RPL_DEBUG_CONFIG ((DWORD)0X00008000) // Config apis
+#define RPL_DEBUG_RESUME ((DWORD)0X00010000) // Resume key usage
+#define RPL_DEBUG_PROFILE ((DWORD)0X00020000) // Profile apis
+#define RPL_DEBUG_WKSTA ((DWORD)0X00040000) // Wksta apis
+#define RPL_DEBUG_ADAPTER ((DWORD)0X00080000) // Adapter apis
+#define RPL_DEBUG_BOOT ((DWORD)0X00100000) // Boot record usage
+#define RPL_DEBUG_VENDOR ((DWORD)0X00200000) // Vendor record usage
+#define RPL_DEBUG_SPECIAL ((DWORD)0X00400000) // for one time bugs
+
+#else // RPL_DEBUG
+
+#define RPL_RETURN( arg) return( arg)
+#define RPL_RETURN( arg) return( arg)
+#define RPL_ASSERT( condition)
+
+#define RplDump( DBG_CONDITION, DBG_PRINT_ARGUMENTS) \
+ do {NOTHING;} while (0)
+
+#endif // RPL_DEBUG
+
diff --git a/private/net/svcdlls/rpl/inc/rpldll.h b/private/net/svcdlls/rpl/inc/rpldll.h
new file mode 100644
index 000000000..4a606f487
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpldll.h
@@ -0,0 +1,144 @@
+/*++
+
+Module:
+
+ rpldll.h
+
+Abstract:
+
+ DLL interface: return codes, data structures & function declarations.
+
+--*/
+
+//
+// Data structures used in the DLL function declarations.
+//
+
+typedef struct _OPEN_INFO {
+ //
+ // Adapter info buffer should be large enough to hold private adapter
+ // info and the maximum amount of memory needed for DLC buffer pool.
+ //
+ PBYTE adapt_info_ptr; // Buffer for adapter private information.
+ DWORD adapt_info_size; // Size of above buffer.
+ BYTE NodeAddress[ NODE_ADDRESS_LENGTH]; // burned-in adapter num
+ DWORD lan_rcb_size; // size of the network specific RCB
+} OPEN_INFO, *POPEN_INFO;
+
+typedef struct _RCB { // Resource Control Block
+ struct _RCB * next_rcb; // the next rcb
+ PBYTE lan_rcb_ptr; // ptr to the network specific resource block
+ DWORD sfr_seq_number; // number of requested packet, set by RPL ROM
+ DWORD fdr_seq_number; // number of the next packet to be sent
+
+ DWORD max_frame; // size of data block
+
+ BYTE ReceivedSfr; // have we received an SFR's from this client
+
+ struct { // bitfield
+
+ BYTE alerted: 1; // SAP F8 got an ALERT frame
+ BYTE spframe: 2; // special frame is last one
+ BYTE rcberr:1; // RECEIVE on SAP F8 got an error
+ BYTE defboot:1; // If DLL supports default boot, DLL sets this
+ // to ask server to use default boot
+ } flags;
+
+ //
+ // "send_flags" is used for FLAGS field in FileDataResponse frame.
+ // In OS/2 days RPL service pretended not to know about the DLC layer,
+ // but even then it manipulated "send_flags" defined here.
+ //
+ struct { // bitfield
+ BYTE reserved :4;
+ BYTE ack_request :1;
+ BYTE locate_enable :1;
+ BYTE xfer_enable :1;
+ BYTE end_of_file :1;
+ } send_flags;
+
+ //
+ // SF_wakeup is an EVENT created in RplGetRcbFromList.
+ //
+ HANDLE SF_wakeup; // event for wakeup
+
+ //
+ // thxsemhdl is an EVENT created in RplWorkerThread.
+ //
+ HANDLE txsemhdl; // event used for transmitting data
+
+ BYTE NodeAddress[ NODE_ADDRESS_LENGTH]; // of remote client
+ WCHAR AdapterName[ 2 * NODE_ADDRESS_LENGTH + 1]; // above in hex UNICODE
+
+ //
+ // volume_id is ASCIIZ string of chosen boot. It is set by the DLL
+ // if protocol supports it.
+ //
+ CHAR volume_id[ 17];
+
+ BOOL SFR; // is SFR thread working on this RCB
+ BOOL Pending; // is RCB in a transient state
+ BOOL RcbIsBad; // if we should get rid of this RCB
+} RCB, *PRCB, **PPRCB;
+
+
+//
+// Functions exported by ..\dll\init.c
+//
+BOOL RplDlcInit(
+ POPEN_INFO pOpenInfo,
+ DWORD rpl_adapter
+ );
+BOOL RplDlcTerm( POPEN_INFO pOpenInfo);
+
+//
+// Functions exported by ..\dll\find.c
+//
+BOOL RplDlcFind(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ );
+
+//
+// Functions exported by ..\dll\found.c
+//
+BOOL RplDlcFound(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb
+ );
+
+//
+// Functions exported by ..\dll\fdr.c
+//
+BOOL RplDlcFdr(
+ POPEN_INFO pOpenInfo,
+ PRCB pRcb,
+ PBYTE data_ptr,
+ WORD length,
+ DWORD locate_addr,
+ DWORD start_addr
+ );
+
+
+//
+// Functions exported by ..\dll\sfr.c
+//
+BOOL RplDlcSfr(
+ POPEN_INFO pOpenInfo,
+ PPRCB HeadRcbList,
+ PCRITICAL_SECTION ProtectRcbList
+ );
+PRCB RplFindRcb(
+ PPRCB HeadRcbList,
+ PBYTE NodeAddress
+ );
+#ifdef RPL_DEBUG
+#define RPL_MUST_FIND 1
+#define RPL_MUST_NOT_FIND 2
+VOID DebugCheckList( PPRCB HeadList, PRCB pInputRcb, DWORD Operation);
+#else
+#define DebugCheckList( HeadList, pInputRcb, Operation)
+#endif
+
+
+
diff --git a/private/net/svcdlls/rpl/inc/rpllib.h b/private/net/svcdlls/rpl/inc/rpllib.h
new file mode 100644
index 000000000..b3361878b
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rpllib.h
@@ -0,0 +1,94 @@
+/*++
+
+Module Name:
+
+ rpllib.h
+
+Abstract:
+
+ Desribes exports from lib subirectory.
+
+ Also used for macros used by several RPL subdirectories - because
+ all of them include this file.
+
+--*/
+
+#define RPL_STRING_TOO_LONG(_x_) (_x_!=NULL && wcslen(_x_)>RPL_MAX_STRING_LENGTH)
+
+//
+// Exports from ..\lib\adapter.c
+//
+BOOL ValidHexName( IN PWCHAR Name, IN DWORD NameLength, IN BOOL MustHaveInput);
+BOOL ValidName( IN PWCHAR Name, IN DWORD MaxNameLength, IN BOOL MustHaveInput);
+
+//
+// Exports from ..\lib\tcpip.c
+//
+DWORD FillTcpIpString( OUT PCHAR Buffer, IN DWORD AddressDword);
+
+//
+// Exports from ..\lib\addkey.c
+//
+DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source);
+
+//
+// Exports from ..\lib\jeterror.c
+//
+DWORD MapJetError( IN JET_ERR JetError);
+
+//
+// Exports from ..\lib\report.c
+//
+VOID RplReportEvent(
+ IN DWORD MessageId,
+ IN LPWSTR InsertionString,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ );
+
+//
+// Exports from ..\lib\cmdline.c
+//
+
+VOID RplPrintf0(
+ IN DWORD MessageId
+ );
+VOID RplPrintf1(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString
+ );
+VOID RplPrintf2(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString1,
+ IN PWCHAR InsertionString2
+ );
+VOID RplPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters,
+ IN DWORD NumParameters
+ );
+VOID RplPrintfID(
+ IN DWORD MessageId,
+ IN DWORD MessageIdInsertion
+ );
+VOID RplSPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters,
+ IN DWORD NumParameters,
+ OUT PWCHAR * MessageStringPtr
+ );
+
+//
+// Exports from ..\lib\reg.c
+//
+
+DWORD RplRegRead(
+ OUT DWORD * pAdapterPolicy,
+ OUT DWORD * pBackupInterval,
+ OUT DWORD * pMaxThreadCount,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ );
+DWORD RplRegSetPolicy(
+ IN DWORD NewAdapterPolicy
+ );
diff --git a/private/net/svcdlls/rpl/inc/rplnames.h b/private/net/svcdlls/rpl/inc/rplnames.h
new file mode 100644
index 000000000..454ccf6a4
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/rplnames.h
@@ -0,0 +1,24 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplnames.h
+
+Abstract:
+
+ Private header file which defines the RPL service names.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 22 - November - 1993
+
+Revision History:
+
+ 22-Nov-1993 vladimv
+ Created
+
+--*/
+
+#define RPL_INTERFACE_NAME TEXT("rplsvc")
diff --git a/private/net/svcdlls/rpl/inc/vendor.h b/private/net/svcdlls/rpl/inc/vendor.h
new file mode 100644
index 000000000..39516f572
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/vendor.h
@@ -0,0 +1,53 @@
+/*++
+
+Module Name:
+
+ vendor.h
+
+Abstract:
+
+ Describes layout of JET database table used for VENDOR structures.
+
+--*/
+
+#ifdef RPLVENDOR_ALLOCATE
+#define EXTERN_VENDOR
+#define INIT_VENDOR( _x) = _x
+#else
+#define EXTERN_VENDOR extern
+#define INIT_VENDOR( _x)
+#endif
+
+//
+// Indices of entries in VendorTable[] - used to describe vendor codes
+//
+#define VENDOR_VendorName 0
+#define VENDOR_VendorComment 1
+#define VENDOR_Flags 2
+#define VENDOR_TABLE_LENGTH 3
+
+RPL_COLUMN_INFO VendorTable[]
+#ifdef RPLVENDOR_ALLOCATE
+ = {
+ { "VendorName", JET_coltypBinary, 0}, // first 6 digits of adapter id
+ { "VendorComment", JET_coltypBinary, 0}, // vendor comment
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLVENDOR_ALLOCATE
+;
+
+//
+// This definition gives wrong result when RPLVENDOR_ALLOCATE is not defined
+//#define VENDOR_TABLE_LENGTH (sizeof(VendorTable)/sizeof(VendorTable[0]))
+//
+
+#define VENDOR_INDEX_VendorName "foo" // + VendorName
+
+#define VENDOR_TABLE_NAME "Vendor"
+#define VENDOR_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define VENDOR_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_VENDOR JET_TABLEID VendorTableId;
+#endif // RPL_RPLCNV
+
diff --git a/private/net/svcdlls/rpl/inc/wksta.h b/private/net/svcdlls/rpl/inc/wksta.h
new file mode 100644
index 000000000..49f58401b
--- /dev/null
+++ b/private/net/svcdlls/rpl/inc/wksta.h
@@ -0,0 +1,105 @@
+/*++
+
+Module Name:
+
+ wksta.h
+
+Abstract:
+
+ Describes layout of JET database table used for WKSTA structures.
+
+--*/
+
+#ifdef RPLWKSTA_ALLOCATE
+#define EXTERN_WKSTA
+#define INIT_WKSTA( _x) = _x
+#else
+#define EXTERN_WKSTA extern
+#define INIT_WKSTA( _x)
+#endif
+
+//
+// WKSTA_LOGON_INPUT_* describe username/password policy during rpl logon
+// on the client side. Depending on the value of this field, user input for
+// username/password during RPL logon will be:
+//
+#define WKSTA_LOGON_INPUT_REQUIRED L'P' // user input is required
+#define WKSTA_LOGON_INPUT_OPTIONAL L'N' // user input is optional
+#define WKSTA_LOGON_INPUT_IMPOSSIBLE L'D' // user input is not solicited
+//
+// WKSTA_SHARING_* describe whether workstation shares or does not share its
+// remote boot disk (i.e. "does it have shared or personal profile").
+//
+#define WKSTA_SHARING_TRUE L'S' // shares remote boot disk
+#define WKSTA_SHARING_FALSE L'P' // does not share remote boot disk
+
+//
+// WKSTA_SHARING_* describe whether workstation shares or does not share its
+// remote boot disk (i.e. "does it have shared or personal profile").
+//
+#define WKSTA_SHARING_TRUE L'S' // shares remote boot disk
+#define WKSTA_SHARING_FALSE L'P' // does not share remote boot disk
+
+//
+// WKSTA_DISABLE_DHCP_* describe whether workstation uses DHCP or not. Note
+// that these flags are relevant only if TCP/IP itself is enabled (i.e. changes
+// to boot block configuration file, config.sys & autoexec.bat have been made).
+//
+
+#define WKSTA_DISABLE_DHCP_FALSE '0' // use DHCP
+#define WKSTA_DISABLE_DHCP_TRUE '1' // do not use DHCP
+
+//
+// Indices of entries in WkstaTable[] - wksta column array.
+//
+#define WKSTA_WkstaName 0
+#define WKSTA_WkstaComment 1
+#define WKSTA_ProfileName 2
+#define WKSTA_BootName 3
+#define WKSTA_FitFile 4
+#define WKSTA_AdapterName 5
+#define WKSTA_TcpIpAddress 6
+#define WKSTA_TcpIpSubnet 7
+#define WKSTA_TcpIpGateway 8
+#define WKSTA_Flags 9
+#define WKSTA_TABLE_LENGTH 10
+
+//
+// We could speed things up by defining AdapterName as JET_coltypCurrency
+//
+
+EXTERN_WKSTA RPL_COLUMN_INFO WkstaTable[ WKSTA_TABLE_LENGTH]
+#ifdef RPLWKSTA_ALLOCATE
+ = {
+ { "WkstaName", JET_coltypBinary, 0}, // wksta name
+ { "WkstaComment", JET_coltypBinary, 0}, // wksta comment
+ { "ProfileName", JET_coltypBinary, 0}, // profile name
+ { "BootName", JET_coltypBinary, 0}, // boot block id - if NULL => consult profile
+ { "FitFile", JET_coltypBinary, 0}, // fit file - if NULL => consult profile
+ { "AdapterName", JET_coltypBinary, 0}, // network address, hex string
+ { "TcpIpAddress", JET_coltypLong, 0}, // tcp/ip address
+ { "TcpIpSubnet", JET_coltypLong, 0}, // subnet tcp/ip address
+ { "TcpIpGateway", JET_coltypLong, 0}, // gateway tcp/ip address
+ { "Flags", JET_coltypLong, 0}
+}
+#endif // RPLWKSTA_ALLOCATE
+;
+//
+// This definition gives wrong result when RPLWKSTA_ALLOCATE is not defined
+//#define WKSTA_TABLE_LENGTH (sizeof(WkstaTable)/sizeof(WkstaTable[0]))
+//
+
+#define WKSTA_INDEX_AdapterName "foo" // + AdapterName
+#define WKSTA_INDEX_WkstaName "goo" // + WkstaName
+#define WKSTA_INDEX_ProfileNameWkstaName "hoo" // + ProfileName + WkstaName
+#define WKSTA_INDEX_BootNameWkstaName "joo" // + BootName + WkstaName
+
+#define WKSTA_TABLE_NAME "Wksta"
+#define WKSTA_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define WKSTA_TABLE_DENSITY 100 // initial density
+
+#ifdef RPL_RPLCNV
+EXTERN_WKSTA JET_TABLEID WkstaTableId;
+#endif // RPL_RPLCNV
+
+
diff --git a/private/net/svcdlls/rpl/lib/adapter.c b/private/net/svcdlls/rpl/lib/adapter.c
new file mode 100644
index 000000000..ff83481eb
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/adapter.c
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Contains:
+ BOOL ValidHexName( IN PWCHAR Name, IN DWORD Length)
+ BOOL ValidName( IN PWCHAR Name, IN DWORD MaxNameLength)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpllib.h"
+#include "icanon.h" // I_NetPathType
+
+
+
+BOOL ValidHexName(
+ IN PWCHAR Name,
+ IN DWORD NameLength,
+ IN BOOL MustHaveInput
+ )
+/*++
+
+Routine Description:
+
+ Returns TRUE if the input string is a hex string of a
+ desired length.
+
+Arguments:
+
+ Name - NULL or NULL terminated hexadecimal string
+ NameLength - meaningfull only if Name is provided
+ MustHaveInput - TRUE if null Name input is not allowed
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ DWORD count;
+
+ if ( Name == NULL) {
+ return( !MustHaveInput); // null Name is OK only if MustHaveInput is FALSE
+ }
+ for ( count = 0; iswxdigit(*Name); count++) {
+ Name++;
+ }
+ if ( *Name != 0 || count != NameLength) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL ValidName(
+ IN PWCHAR Name,
+ IN DWORD MaxNameLength,
+ IN BOOL MustHaveInput
+ )
+/*++
+
+Routine Description:
+
+ Returns TRUE if Name is valid. When Name is not NULL,
+ then is must be a valid path component for RPL purposes.
+
+Arguments:
+
+ Name - NULL or NULL terminated RPL path string
+ MaxNameLength - meaningfull only if Name is provided
+ MustHaveInput - TRUE if null Name input is not allowed
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ DWORD Length;
+ DWORD PathType = 0;
+
+ if ( Name == NULL) {
+ return( !MustHaveInput); // null Name is OK only if MustHaveInput is FALSE
+ }
+
+ Length = wcslen( Name);
+
+ if ( Length == 0 || Length > MaxNameLength) {
+ return( FALSE);
+ }
+ //
+ // Do not allow leading spaces nor "." or ".." (according to old rplmgr
+ // code these are all very bad).
+ //
+ if ( iswspace( *Name) || *Name==L'.') {
+ return( FALSE);
+ }
+ if ( wcsspn( Name, L"\f\n\r\t\v\\ ") != 0) {
+ return( FALSE);
+ }
+ if ( I_NetPathType( NULL, Name, &PathType, 0) != NO_ERROR
+ || PathType != ITYPE_PATH_RELND ) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
diff --git a/private/net/svcdlls/rpl/lib/addkey.c b/private/net/svcdlls/rpl/lib/addkey.c
new file mode 100644
index 000000000..681be066e
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/addkey.c
@@ -0,0 +1,31 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ addkey.c
+
+Abstract:
+
+ Contains:
+ DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+DWORD AddKey( OUT PCHAR Target, IN CHAR Prefix, IN PCHAR Source)
+{
+ DWORD Size;
+ Target[ 0] = Prefix;
+ Size = 1 + strlen( Source); // copy terminating null
+ memcpy( Target + sizeof( Prefix), Source, Size);
+ return( Size + sizeof( Prefix));
+}
diff --git a/private/net/svcdlls/rpl/lib/cmdline.c b/private/net/svcdlls/rpl/lib/cmdline.c
new file mode 100644
index 000000000..875883bcb
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/cmdline.c
@@ -0,0 +1,249 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ cmdline.c
+
+Abstract:
+
+ Contains:
+ VOID RplPrintfXXX()
+
+Author:
+
+ Jon Newman (jonn) 20 - April - 1994
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+//
+// Must fit within FORMAT_MESSAGE_MAX_WIDTH_MASK
+//
+#define MAX_LINE_WIDTH 80
+
+
+VOID RplPrintf0(
+ IN DWORD MessageId
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RplPrintfN( MessageId, NULL, 0 );
+}
+
+VOID RplPrintf1(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString // note: Unicode
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+ InsertionString - asciiz string (may be NULL)
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RplPrintfN( MessageId, &InsertionString, 1 );
+}
+
+
+VOID RplPrintf2(
+ IN DWORD MessageId,
+ IN PWCHAR InsertionString1, // note: Unicode
+ IN PWCHAR InsertionString2 // note: Unicode
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ InsertionString1 - asciiz string (may be NULL)
+ InsertionString2 - asciiz string (may be NULL)
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR ParamArray[2];
+ ParamArray[0] = InsertionString1;
+ ParamArray[1] = InsertionString2;
+ RplPrintfN( MessageId, ParamArray, 2 );
+}
+
+
+VOID RplPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters, // note: Unicode
+ IN DWORD NumParameters
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ Parameters - array of pointers to parameters
+
+ NumParameters - number of parameters in array
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR MessageString = NULL;
+ DWORD Error = NO_ERROR;
+
+ (void) RplSPrintfN( MessageId,
+ Parameters,
+ NumParameters,
+ &MessageString );
+
+ if (MessageString != NULL) {
+ wprintf( MessageString );
+ }
+
+ if (MessageString != NULL) {
+ LocalFree( MessageString );
+ MessageString = NULL;
+ }
+}
+
+
+VOID RplPrintfID(
+ IN DWORD MessageId,
+ IN DWORD MessageIdInsertion
+ )
+/*++
+
+Routine Description:
+
+ Writes a message to STDOUT.
+
+Arguments:
+
+ MessageId - Message ID
+
+ MessageIdInsertion - Message ID to be inserted as %1 into MessageId
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWCHAR MessageString = NULL;
+
+ RplSPrintfN( MessageIdInsertion, NULL, 0, &MessageString );
+
+ if (MessageString != NULL) {
+ RplPrintf1( MessageId, MessageString );
+ }
+
+ if (MessageString != NULL) {
+ LocalFree( MessageString );
+ MessageString = NULL;
+ }
+}
+
+
+VOID RplSPrintfN(
+ IN DWORD MessageId,
+ IN PWCHAR * Parameters, // note: Unicode
+ IN DWORD NumParameters,
+ OUT PWCHAR * MessageStringPtr
+ )
+/*++
+
+Routine Description:
+
+ Retrieves a message
+
+Arguments:
+
+ MessageId - Message ID
+
+ Parameters - array of pointers to parameters
+
+ NumParameters - number of parameters in array
+
+ MessageStringPtr - message returned, must be LocalFree'd by caller
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+
+ if (MessageStringPtr == NULL) {
+#ifdef RPL_DEBUG
+ wprintf( L"RplSPrintfN: bad parameter\n" );
+#endif
+ return;
+ }
+
+ if ( 0 == FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_HMODULE
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY
+ | MAX_LINE_WIDTH,
+ NULL,
+ MessageId,
+ 0,
+ (LPTSTR)(MessageStringPtr), // ALLOCATE_BUFFER
+ NumParameters,
+ (va_list *)Parameters) ) { // ARGUMENT_ARRAY
+#ifdef RPL_DEBUG
+ Error = GetLastError();
+ wprintf( L"RplSPrintfN: FormatMessage error %d\n", Error );
+#endif
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error != NO_ERROR && *MessageStringPtr != NULL) {
+ LocalFree( *MessageStringPtr );
+ *MessageStringPtr = NULL;
+ }
+}
diff --git a/private/net/svcdlls/rpl/lib/jeterror.c b/private/net/svcdlls/rpl/lib/jeterror.c
new file mode 100644
index 000000000..70bb8f1a1
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/jeterror.c
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ jeterror.c
+
+Abstract:
+
+ Contains:
+ DWORD MapJetError( IN JET_ERR JetError)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+//
+// BUGBUG ERROR_RPL_JET_ERROR should be a part of winerr.h
+// Although RPL apis follow NET convention, they really use WINDOWS error
+// codes. This is somewhat inconsistent.
+//
+#define ERROR_RPL_JET_ERROR 0x7777 // BUGBUG arbitrary value for now
+
+
+DWORD MapJetError( IN JET_ERR JetError)
+/*++
+
+Routine Description:
+
+ This function maps the Jet database errors to Windows error.
+
+Arguments:
+
+ JetError - an error JET function call.
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ if( JetError == JET_errSuccess ) {
+ return( ERROR_SUCCESS);
+ } else if ( JetError < 0) {
+ DWORD Error;
+ switch( JetError ) {
+ case JET_errNoCurrentRecord:
+ Error = ERROR_NO_MORE_ITEMS;
+ break;
+
+ case JET_errRecordNotFound: // record not found
+ default:
+ Error = ERROR_RPL_JET_ERROR;
+ }
+ return(Error);
+ } else {
+ return( ERROR_SUCCESS);
+ }
+}
diff --git a/private/net/svcdlls/rpl/lib/local.h b/private/net/svcdlls/rpl/lib/local.h
new file mode 100644
index 000000000..927f8d163
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/local.h
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load library.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_lsINFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h> // JET_ERR
+
+#include "rpldebug.h"
+#include "rpllib.h"
diff --git a/private/net/svcdlls/rpl/lib/makefile b/private/net/svcdlls/rpl/lib/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/lib/reg.c b/private/net/svcdlls/rpl/lib/reg.c
new file mode 100644
index 000000000..181fd207b
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/reg.c
@@ -0,0 +1,271 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ reg.c
+
+Abstract:
+
+ Registry _access routines for RPL server and related components
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+ Moved from server by Jon Newman 26 - April - 1994
+
+--*/
+
+#include "local.h"
+
+#define RPL_REGISTRY_PARAMETERS L"System\\CurrentControlSet\\Services\\RemoteBoot\\Parameters"
+#define RPL_REGISTRY_DIRECTORY L"Directory"
+#define RPL_REGISTRY_STARTUP L"Startup"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+#define RPL_DEFAULT_STARTUP 0
+#define RPL_DEFAULT_BACKUP_INTERVAL 24
+#define RPL_DEFAULT_MAX_WORKER_COUNT 10
+#define RPL_REGISTRY_BACKUP_INTERVAL L"BackupInterval"
+#define RPL_REGISTRY_THREAD_COUNT L"maxthreads"
+
+
+DWORD RplRegReadValue(
+ IN HKEY Key,
+ IN DWORD DefaultValue,
+ IN PWCHAR Name,
+ OUT DWORD * pValue
+ )
+{
+ DWORD Size;
+ DWORD Type;
+ DWORD Error;
+
+ Size = sizeof( DWORD);
+ Error = RegQueryValueEx( Key, Name, NULL,
+ &Type, (PBYTE)pValue, &Size);
+ if ( Error == NO_ERROR) {
+ if ( Type != REG_DWORD || Size != sizeof( DWORD)) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Type = 0x%x, Value = 0x%x, Size=%d",
+ Type, *pValue, Size));
+ }
+ } else if (Error == ERROR_FILE_NOT_FOUND) {
+ Error = NO_ERROR;
+ *pValue = DefaultValue;
+ } else {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ }
+ return( Error);
+}
+
+
+
+DWORD RplRegReadDirectory(
+ IN HKEY Key,
+ IN PWCHAR DefaultValue,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ )
+{
+ WCHAR Buffer[ MAX_PATH];
+ PWCHAR Value;
+ DWORD Length;
+ DWORD Error;
+ DWORD Size;
+ DWORD Type;
+
+ Size = sizeof( Buffer);
+ Error = RegQueryValueEx( Key, RPL_REGISTRY_DIRECTORY, NULL,
+ &Type, (PBYTE)Buffer, &Size);
+ if ( Error == NO_ERROR) {
+ //
+ // We have the data. First validate the data, then append the
+ // backslash if needed.
+ //
+ Value = Buffer;
+ if ( Type != REG_EXPAND_SZ || (Size&1) != 0 || Size < sizeof(L"c:")) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Type2 = 0x%x, Value = 0x%x, Size=%d",
+ Type, Value, Size));
+ return( Error);
+ }
+ Length = Size / sizeof(WCHAR) - 1;
+ if ( Value[ Length] != 0 || Length != wcslen(Value)) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ Length, Value, Size));
+ return( Error);
+ }
+ if ( Value[ Length - 1] != L'\\') {
+ //
+ // Need to append the backslash.
+ //
+ if ( ( Length + 1) >= MAX_PATH) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ Length, Value, Size));
+ return( Error); // not enough space
+ }
+ Value[ Length] = L'\\';
+ Value[ ++Length] = 0;
+ }
+ } else if ( Error == ERROR_FILE_NOT_FOUND ) {
+ Value = DefaultValue;
+ } else {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+
+ //
+ // Length returned by ExpandEnvironmentalStrings includes terminating
+ // NULL byte.
+ //
+ Length = ExpandEnvironmentStrings( Value, DirectoryBuffer, *pDirectoryBufferSize);
+ if ( Length == 0) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ if ( Length > (*pDirectoryBufferSize)
+ || Length > MAX_PATH || Length != wcslen(DirectoryBuffer) + 1) {
+ Error = NERR_RplBadRegistry;
+ RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( Error);
+ }
+ *pDirectoryBufferSize = (DWORD)(Length - 1);
+ return( NO_ERROR);
+}
+
+
+
+DWORD RplRegRead(
+ OUT DWORD * pStartup,
+ OUT DWORD * pBackupInterval,
+ OUT DWORD * pMaxThreadCount,
+ OUT PWCHAR DirectoryBuffer,
+ IN OUT DWORD * pDirectoryBufferSize
+ )
+{
+ DWORD Error;
+ HKEY Key;
+ BOOL HaveKey = FALSE;
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ // This will fail if the RPL registry tree is absent, that is,
+ // if RPL is not installed.
+ //
+ Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RPL_REGISTRY_PARAMETERS,
+ 0, KEY_READ, &Key);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+ HaveKey = TRUE;
+
+ //
+ // It is OK if values in parameters subtree are absent, but present
+ // values should be valid. This is why each read routine receives
+ // default value as the second parameter.
+ //
+
+ if ( pStartup != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_STARTUP, RPL_REGISTRY_STARTUP,
+ pStartup);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( pBackupInterval != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_BACKUP_INTERVAL,
+ RPL_REGISTRY_BACKUP_INTERVAL, pBackupInterval);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Registry contains backup interval in hours, convert it to
+ //
+ //
+ }
+
+ if ( pMaxThreadCount != NULL) {
+ Error = RplRegReadValue( Key, RPL_DEFAULT_MAX_WORKER_COUNT,
+ RPL_REGISTRY_THREAD_COUNT, pMaxThreadCount);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( DirectoryBuffer != NULL && pDirectoryBufferSize != NULL
+ && (*pDirectoryBufferSize) != 0 ) {
+ Error = RplRegReadDirectory( Key, RPL_DEFAULT_DIRECTORY,
+ DirectoryBuffer, pDirectoryBufferSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if ( HaveKey == TRUE) {
+ (VOID)RegCloseKey( Key);
+ }
+ if ( Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplRegistry, NULL, sizeof(DWORD), &Error );
+ }
+ return( Error);
+}
+
+
+DWORD RplRegSetPolicy( DWORD NewStartup)
+{
+ DWORD Error;
+ HKEY Key;
+ BOOL HaveKey = FALSE;
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ // This will fail if the RPL registry tree is absent, that is,
+ // if RPL is not installed.
+ //
+ Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, RPL_REGISTRY_PARAMETERS,
+ 0, KEY_SET_VALUE, &Key);
+ if ( Error != NO_ERROR){
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+ HaveKey = TRUE;
+
+ //
+ // Now set the Startup value
+ //
+ Error = RegSetValueEx( Key, RPL_REGISTRY_STARTUP, 0,
+ REG_DWORD, (PBYTE)&NewStartup, sizeof(NewStartup));
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ("Error = %d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+ if ( HaveKey == TRUE) {
+ (VOID)RegCloseKey( Key);
+ }
+
+ if ( Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplRegistry, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/lib/report.c b/private/net/svcdlls/rpl/lib/report.c
new file mode 100644
index 000000000..2364ed2e0
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/report.c
@@ -0,0 +1,111 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Contains:
+ VOID RplReportEvent(
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+
+VOID RplReportEvent(
+ IN DWORD MessageId,
+ IN LPWSTR InsertionString,
+ IN DWORD RawDataBufferLength OPTIONAL,
+ IN LPVOID RawDataBuffer
+ )
+/*++
+
+Routine Description:
+
+ Writes an error message and ascii string to the error log. Also,
+ writes the data in the data buf if there are any.
+
+Arguments:
+
+ MessageId - Message ID
+
+ InsertionString - at most one asciiz string (may be NULL)
+
+ RawDataBufferLength - size of data to be printed from the auxiliary data
+ buffer. May be zero, in which case the actual value depends on
+ "RawDataBuffer". It is 0 if "RawDataBuffer" is NULL, else it is
+ "sizeof( wchar) * wcslen( RawDataBuffer)".
+
+ RawDataBuffer - data buffer containing secondary error code & some
+ other useful info. Must be NULL terminated string or NULL if
+ "RawDataBufferLength" is zero.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ WORD NumberOfStrings;
+ LPWSTR InsertionStringArray[ 1];
+ LPWSTR * PointerToStrings;
+ HANDLE logHandle;
+
+ logHandle = RegisterEventSource( NULL, RPL_EVENTLOG_NAME);
+
+ // If the event log cannot be opened, just return.
+
+ if ( logHandle == NULL) {
+#ifdef NOT_YET
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "ReportEvent: RegisterEventSource() failed with error %d",
+ GetLastError()));
+#endif // NOT_YET
+ return;
+ }
+
+ if ( InsertionString == NULL) {
+ PointerToStrings = NULL;
+ NumberOfStrings = 0;
+ } else {
+ InsertionStringArray[ 0] = InsertionString;
+ PointerToStrings = InsertionStringArray;
+ NumberOfStrings = 1;
+ }
+
+ //
+ // Use default for RawDataBufferLength if caller requested us so.
+ //
+ if ( RawDataBufferLength == 0 && RawDataBuffer != NULL) {
+ RawDataBufferLength = sizeof( TCHAR) * wcslen( (LPWSTR)RawDataBuffer);
+ }
+
+ if ( !ReportEvent(
+ logHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId, // event id
+ NULL, // user SID. We're local system - uninteresting
+ NumberOfStrings, // number of strings
+ RawDataBufferLength, // raw data size
+ PointerToStrings, // string array
+ RawDataBuffer // raw data buffer
+ )) {
+#ifdef NOT_YET
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "ReportEvent: fails with error %d", GetLastError()));
+#endif // NOT_YET
+ }
+
+ DeregisterEventSource( logHandle);
+}
diff --git a/private/net/svcdlls/rpl/lib/sources b/private/net/svcdlls/rpl/lib/sources
new file mode 100644
index 000000000..2be59e4ef
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/sources
@@ -0,0 +1,36 @@
+MAJORCOMP=net
+MINORCOMP=rpllib
+
+TARGETPATH=obj
+TARGETNAME=rpllib
+TARGETTYPE=LIBRARY
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ report.c \
+ cmdline.c \
+ adapter.c \
+ tcpip.c \
+ jeterror.c \
+ reg.c \
+ addkey.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+
+
diff --git a/private/net/svcdlls/rpl/lib/tcpip.c b/private/net/svcdlls/rpl/lib/tcpip.c
new file mode 100644
index 000000000..b45b2f8d3
--- /dev/null
+++ b/private/net/svcdlls/rpl/lib/tcpip.c
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tcpip.c
+
+Abstract:
+
+ Contains:
+ DWORD FillTcpIpString( OUT PCHAR Buffer, IN DWORD AddressDword)
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpllib.h"
+#include "winsock.h" // for INADDR_NONE
+
+DWORD FillTcpIpString(
+ OUT PCHAR Buffer,
+ IN DWORD AddressDword
+ )
+/*++
+ AddressDword is in a host byte order. E.g. dword 0x11223344
+ corresponds to 11.22.33.44 string.
+--*/
+{
+ if ( AddressDword == INADDR_NONE) {
+ strcpy( Buffer, "~ ");
+ } else {
+#ifdef NOT_YET
+ PBYTE p = (PBYTE)&AddressDword;
+ //
+ // For dword 0x11223344 we have p[0] = 44, etc.
+ //
+ // We do not do:
+ //
+ // ipaddr.s_addr = htonl( AddressDword)
+ // string = inet_ntoa( ipaddr)
+ // strcpy( Buffer, string)
+ //
+ // because inet_ntoa() allocates buffer for string.
+ //
+ sprintf( Buffer, "%d.%d.%d.%d ", p[3], p[2], p[1], p[0]);
+#else
+ struct in_addr InAddr; // Internet address structure
+ PCHAR String;
+
+ // Convert the host address to network byte order
+ InAddr.s_addr = htonl( AddressDword);
+
+ // Convert the IP address value to a string
+ String = inet_ntoa( InAddr) ;
+
+ // Copy the string to our buffer & append a space.
+ strcpy( Buffer, String);
+ strcat( Buffer, " ");
+#endif
+ }
+ return( strlen( Buffer));
+}
diff --git a/private/net/svcdlls/rpl/makefil0 b/private/net/svcdlls/rpl/makefil0
new file mode 100644
index 000000000..fc6bb29d7
--- /dev/null
+++ b/private/net/svcdlls/rpl/makefil0
@@ -0,0 +1,73 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+IDL_NAME = rplsvc
+CLIENT_H = rplsvc_c.h
+SERVER_H = rplsvc_s.h
+CLIENT_ACF = rplsvc_c.acf
+SERVER_ACF = rplsvc_s.acf
+
+!IFNDEF BASEDIR
+BASEDIR=\nt
+!ENDIF
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SDKINC = $(BASEDIR)\public\sdk\inc
+NETINC = $(BASEDIR)\private\net\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+PRIVINC = $(BASEDIR)\private\inc
+RPLINC = inc
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I$(NETINC) -I$(RPLINC)
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c client\$(CLIENT_H)
+SERVER_TARGETS = server\$(IDL_NAME)_s.c server\$(SERVER_H)
+TARGETS = $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+EXTRN_DEPENDS = $(SDKINC)\lmcons.h \
+ $(SDKINC)\windef.h \
+ $(SDKINC)\lmrpl.h \
+ inc\i_lmrpl.h \
+ inc\imports.h
+
+CLIENT_FLAGS = -acf $(CLIENT_ACF) -header $(CLIENT_H) -oldnames
+SERVER_FLAGS = -acf $(SERVER_ACF) -header $(SERVER_H) -oldnames
+
+CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) $(C_DEFINES) $(NET_C_DEFINES)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(CLIENT_TARGETS) $(SERVER_TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS) .\$(CLIENT_ACF)
+ midl -Oi -server none -error allocation -error ref -ms_ext -c_ext $(CPP) $(CLIENT_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(CLIENT_H) copy $(CLIENT_H) .\client & del $(CLIENT_H)
+
+$(SERVER_TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS) .\$(SERVER_ACF)
+ midl -client none -error stub_data -error allocation -error ref -ms_ext -c_ext $(CPP) $(SERVER_FLAGS) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_H) copy $(SERVER_H) .\server & del $(SERVER_H)
diff --git a/private/net/svcdlls/rpl/makefile b/private/net/svcdlls/rpl/makefile
new file mode 100644
index 000000000..d30e64184
--- /dev/null
+++ b/private/net/svcdlls/rpl/makefile
@@ -0,0 +1,12 @@
+# BUGBUG This makefile is needed so that build.exe would not report an
+# BUGBUG a bogus error. There is probably a better way to achieve this.
+# ( The bogus error is reported when linking rpl directory:
+# NMAKE U1064: MAKEFILE not found & no target specified )
+#
+
+all:
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: all
diff --git a/private/net/svcdlls/rpl/rplcnv/local.h b/private/net/svcdlls/rpl/rplcnv/local.h
new file mode 100644
index 000000000..b5e032c06
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/local.h
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLSTART
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+
+#include "rplmgrd.h"
diff --git a/private/net/svcdlls/rpl/rplcnv/makefile b/private/net/svcdlls/rpl/rplcnv/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplcnv/rplcnv.c b/private/net/svcdlls/rpl/rplcnv/rplcnv.c
new file mode 100644
index 000000000..218f90581
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/rplcnv.c
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplcnv.c
+
+Abstract:
+
+ Converts old style (OS/2) files RPL.MAP & RPLMGR.INI
+ into a new style database to be used with NT rpl server.
+
+Author:
+
+ Jon Newman (jonn) 02 - February - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+ I_NetRplCmd_ConvertDatabase( NULL);
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/rplcnv/sources b/private/net/svcdlls/rpl/rplcnv/sources
new file mode 100644
index 000000000..c298a8272
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplcnv/sources
@@ -0,0 +1,27 @@
+MAJORCOMP=net
+MINORCOMP=rplcnv
+
+TARGETPATH=obj
+TARGETNAME=rplcnv
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\convert\obj\*\rplmgrd.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplcnv.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplinst/debug.c b/private/net/svcdlls/rpl/rplinst/debug.c
new file mode 100644
index 000000000..468b04969
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/debug.c
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module. RpldDebugPrint() was adapted from TelnetDebugPrint()
+ in net\sockets\tcpcmd\telnet\debug.c.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "debug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+#define DEBUG_RECORD_SIZE 0x80
+#define DEBUG_BUFFER_SIZE 0x20000 // 2*64k, must be a power of 2
+#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1)
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+PCHAR RG_DebugBuffer;
+CRITICAL_SECTION RG_ProtectDebug;
+DWORD RG_DebugOffset;
+//
+// This buffer is used even when RG_DebugBuffer is not NULL.
+//
+char RG_DebugPublicBuffer[ 120];
+
+
+VOID RplDebugDelete( VOID)
+{
+ if ( RG_DebugBuffer != NULL) {
+ (VOID)GlobalFree( RG_DebugBuffer);
+ }
+ DeleteCriticalSection( &RG_ProtectDebug);
+}
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID RplDebugInitialize( VOID)
+{
+ //
+ // Set RG_Assert to 0. Without this the very first use of
+ // RplDump() will lead to an assert.
+ //
+ RG_Assert = 0;
+
+// RG_DebugLevel = RPL_DEFAULT_DEBUG_LEVEL;
+ RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL & ~RPL_DEBUG_MEMALLOC
+ & ~RPL_DEBUG_FLOWINIT & ~RPL_DEBUG_FDR & ~RPL_DEBUG_SFR;
+// RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL;
+
+
+ RG_DebugBuffer = (PCHAR)GlobalAlloc(
+ GMEM_FIXED,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE
+ );
+ if ( RG_DebugBuffer != NULL) {
+ RtlFillMemory(
+ RG_DebugBuffer,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE,
+ '*'
+ );
+ RG_DebugOffset = 0;
+ }
+ memcpy( RG_DebugPublicBuffer, RPL_PROMPT, sizeof( RPL_PROMPT) -1);
+
+ InitializeCriticalSection( &RG_ProtectDebug);
+}
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ EnterCriticalSection( &RG_ProtectDebug);
+
+ if ( RG_Assert == 0 && RG_DebugBuffer != NULL) {
+ RtlZeroMemory( RG_DebugBuffer + RG_DebugOffset, DEBUG_RECORD_SIZE);
+ vsprintf( RG_DebugBuffer + RG_DebugOffset, format, arglist);
+ RG_DebugOffset += DEBUG_RECORD_SIZE;
+ RG_DebugOffset &= DEBUG_BUFFER_MASK;
+ } else {
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer);
+ }
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ KdPrint(( "[RplSvc] Running checked version of service on a free build.\n"));
+ }
+ while ( RG_Assert != 0) {
+ KdPrint(( "[RplSvc] Debug, then reset RG_Assert to 0.\n"));
+ DbgUserBreakPoint();
+ Sleep( RG_Assert * 30000); // sleep for 30 seconds
+ }
+
+ LeaveCriticalSection( &RG_ProtectDebug);
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/rplinst/debug.h b/private/net/svcdlls/rpl/rplinst/debug.h
new file mode 100644
index 000000000..31706c1fe
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/debug.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ Exports from debug.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPL_DEBUG
+VOID RplDebugInitialize( VOID);
+VOID RplDebugDelete( VOID);
+#endif // RPL_DEBUG
diff --git a/private/net/svcdlls/rpl/rplinst/local.h b/private/net/svcdlls/rpl/rplinst/local.h
new file mode 100644
index 000000000..ebdc7068b
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/local.h
@@ -0,0 +1,77 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLINST
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+#include <rpldebug.h>
+
+#include "rplmgrd.h"
+#include "nlstxt.h"
+
+#include <lmshare.h>
+
+#define RPLFILES_STRING L"RPLFILES"
+#define RPL_SERVICE_NAME L"RemoteBoot"
+#define RPL_REMARK L"RemoteBoot service share"
+
+#define RPL_BUGBUG_ERROR 10999 // need to get rid of these
+
+WCHAR RG_Directory[ PATHLEN+1]; // path to rpl service disk data
+DWORD RG_DirectoryLength; // length of the above path
diff --git a/private/net/svcdlls/rpl/rplinst/makefile b/private/net/svcdlls/rpl/rplinst/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplinst/makefile.inc b/private/net/svcdlls/rpl/rplinst/makefile.inc
new file mode 100644
index 000000000..f1d8b42fd
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/makefile.inc
@@ -0,0 +1,8 @@
+# templated from portuas JonN 1/14/94
+nlstxt.mc: rplimsg.h
+ mapmsg -p RPLI RPLI_BASE rplimsg.h > nlstxt.mc
+
+rplinst.rc: nlstxt.rc msg00001.bin
+
+nlstxt.h nlstxt.rc msg00001.bin: nlstxt.mc
+ mc -v nlstxt.mc
diff --git a/private/net/svcdlls/rpl/rplinst/querydir.c b/private/net/svcdlls/rpl/rplinst/querydir.c
new file mode 100644
index 000000000..64e9596c1
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/querydir.c
@@ -0,0 +1,136 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ querydir.c
+
+Abstract:
+
+ Contains I_NetRpl_QueryDirectory
+
+Author:
+
+ Jon Newman 13 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+
+#define RPL_REGISTRY_PARAMETERS L"System\\CurrentControlSet\\Services\\RemoteBoot\\Parameters"
+#define RPL_REGISTRY_DIRECTORY L"Directory"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+
+// BUGBUG server\rplmain.c should call this routine
+
+
+DWORD I_NetRpl_QueryDirectory( OUT LPWSTR pchFilePath,
+ OUT LPDWORD pcchFilePathLength)
+{
+ DWORD Error;
+ BYTE Buffer[ (PATHLEN + 1) * sizeof(WCHAR)];
+ PWCHAR Value;
+ HKEY Key;
+ DWORD Length;
+ DWORD Type;
+ DWORD Size;
+
+ // RPL_ASSERT( pchFilePath != NULL);
+
+ //
+ // First open the RPL service parameters registry tree.
+ //
+ Error = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ RPL_REGISTRY_PARAMETERS,
+ 0,
+ KEY_READ,
+ &Key
+ );
+ if (Error != ERROR_SUCCESS){
+ // RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ // Length = sizeof( Buffer); This is totally wrong! Has Rl Server been
+ // using the default value this whole time?
+ // Length = sizeof( Buffer);
+ Size = sizeof( Buffer);
+ Error = RegQueryValueEx(
+ Key,
+ RPL_REGISTRY_DIRECTORY,
+ NULL,
+ &Type,
+ Buffer,
+ &Size
+ );
+ (VOID)RegCloseKey( Key);
+ if ( Error == NO_ERROR) {
+ //
+ // We have the data. First validate the data, then append the
+ // backslash if needed.
+ //
+ Value = (PWCHAR)Buffer;
+ if ( Type != REG_EXPAND_SZ || (Size&1) != 0 || Size < sizeof(L"c:")) {
+ // RplDump( ++RG_Assert, ("Type = 0x%x, Value = 0x%x, Size=%d",
+ // Type, Value, Size));
+ return( RPL_BUGBUG_ERROR);
+ }
+ Length = Size / sizeof(WCHAR) - 1;
+ if ( Value[ Length] != 0 || Length != wcslen(Value)) {
+ // RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ // Length, Value, Size));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if ( Value[ Length - 1] != L'\\') {
+ //
+ // Need to append the backslash.
+ //
+ if ( Length >= PATHLEN) {
+ // RplDump( ++RG_Assert, ("Length = %d, Value = 0x%x, Size=%d",
+ // Length, Value, Size));
+ return( RPL_BUGBUG_ERROR); // not enough space
+ }
+ Value[ Length] = L'\\';
+ Value[ ++Length] = 0;
+ }
+ } else {
+ Value = RPL_DEFAULT_DIRECTORY;
+ }
+
+ //
+ // Length returned by ExpandEnvironmentalStrings includes terminating
+ // NULL byte.
+ //
+ // This code was wrong!! The third parameter to ExpandEnvironmentStrings
+ // should be a character count, not a byte count.
+ //
+ // Length = ExpandEnvironmentStrings( Value, RG_Directory, sizeof( RG_Directory));
+ Length = ExpandEnvironmentStrings( Value, pchFilePath, PATHLEN+1);
+ if ( Length == 0) {
+ Error = GetLastError();
+ // RplDump( ++RG_Assert, ("Error = %d", Error));
+ return( Error);
+ }
+ if ( Length > PATHLEN+1) {
+ // RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if ( Length != wcslen( pchFilePath) + 1) {
+ // RplDump( ++RG_Assert, ("Length = %d", Length));
+ return( RPL_BUGBUG_ERROR);
+ }
+ if (pcchFilePathLength != NULL) {
+ *pcchFilePathLength = (DWORD)(Length - 1);
+ }
+ return( NO_ERROR);
+}
diff --git a/private/net/svcdlls/rpl/rplinst/rplimsg.h b/private/net/svcdlls/rpl/rplinst/rplimsg.h
new file mode 100644
index 000000000..f66f2c940
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplimsg.h
@@ -0,0 +1,55 @@
+#define RPLI_BASE 11000 /* RPLI Messages start here */
+#define RPLI_MAX 11999 /* RPLI Messages end here */
+
+#define RPLI_CreatingShare (RPLI_BASE + 0)
+ /*
+ *Creating RPLFILES share...
+ */
+#define RPLI_ErrorCreatingShare (RPLI_BASE + 1)
+ /*
+ *Error creating RPLFILES share:%n%1!ls!
+ */
+#define RPLI_ConfiguringService (RPLI_BASE + 2)
+ /*
+ *Making RPL service autostart...
+ */
+#define RPLI_ErrorConfiguringService (RPLI_BASE + 3)
+ /*
+ *Error making RPL service autostart:%n%1!ls!
+ */
+#define RPLI_RPLINSTcomplete (RPLI_BASE + 4)
+ /*
+ *RPLINST completed successfully!
+ */
+#define RPLI_ErrorReadingRPLDirectory (RPLI_BASE + 5)
+ /*
+ *The registry key RPL\\Parameters value Directory could not be read due to the following error:%n%1!ls!
+ */
+#define RPLI_ErrorReadingString (RPLI_BASE + 6)
+ /*
+ *Could not read string
+ */
+#define RPLI_SettingPermissions (RPLI_BASE + 7)
+ /*
+ *Making RPL service autostart...
+ */
+#define RPLI_ErrorSettingPermissions (RPLI_BASE + 8)
+ /*
+ *The file permissions on the RPL file tree could not be set due to the following error:%n%1!ls!
+ */
+#define RPLI_CreatingRPLUSER (RPLI_BASE + 9)
+ /*
+ *Creating the RPLUSER group...
+ */
+#define RPLI_ErrorCreatingRPLUSER (RPLI_BASE + 10)
+ /*
+ *The RPLUSER group could not be created due to the following error:%n%1!ls!
+ */
+#define RPLI_CheckingWkstaAccts (RPLI_BASE + 11)
+ /*
+ *Checking the workstation accounts...
+ */
+#define RPLI_ErrorCheckingWkstaAccts (RPLI_BASE + 12)
+ /*
+ *The following error occurred checking the workstation accounts:%n%1!ls!
+ */
diff --git a/private/net/svcdlls/rpl/rplinst/rplinst.c b/private/net/svcdlls/rpl/rplinst/rplinst.c
new file mode 100644
index 000000000..38c018a30
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplinst.c
@@ -0,0 +1,137 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplinst.c
+
+Abstract:
+
+ Secondary setup activity, including
+
+ Establish RPLFILES share
+
+ Set up permissions on RPLFILES tree
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Jon Newman (jonn) 13 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+#include "security.h"
+
+WCHAR RG_Directory[ PATHLEN+1];
+DWORD RG_DirectoryLength;
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+ DWORD dwErr = NO_ERROR;
+ RPL_HANDLE hRpl = NULL;
+ WCHAR awchBase[ MAX_PATH ];
+
+ dwErr = I_NetRpl_QueryDirectory( RG_Directory, &RG_DirectoryLength);
+ if ( dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorReadingRPLDirectory, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_CreatingRPLUSER);
+
+ dwErr = RplAddRPLUSERGroup();
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCreatingRPLUSER, dwErr);
+ goto cleanup;
+ }
+
+ dwErr = NetRplOpen( NULL, &hRpl );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCheckingWkstaAccts, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_CheckingWkstaAccts);
+
+ dwErr = RplCheckWkstaAccounts( hRpl );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorCheckingWkstaAccts, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_SettingPermissions);
+
+ ASSERT( RG_DirectoryLength < MAX_PATH );
+ ASSERT( RG_DirectoryLength == lstrlenW(RG_DirectoryLength) );
+ ASSERT( RG_Directory[ RG_DirectoryLength-1 ] == L'\\' );
+ lstrcpyW( awchBase, RG_Directory );
+ awchBase[ RG_DirectoryLength-1 ] = L'\0';
+
+ dwErr = RplSetPermsOnTree( awchBase );
+ if (dwErr != NO_ERROR) {
+ I_NetRplCmd_WriteError( STDERR, RPLI_ErrorSettingPermissions, dwErr);
+ goto cleanup;
+ }
+
+ I_NetRplCmd_WriteMessage( STDOUT, RPLI_RPLINSTcomplete);
+
+cleanup:
+
+ if (hRpl != NULL) {
+ NetRplClose( hRpl );
+ }
+
+ return( dwErr);
+}
+
+
+//
+// allows us to use original security.c code
+//
+NET_API_STATUS NET_API_FUNCTION
+MyNetrRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN OUT LPRPL_WKSTA_ENUM WkstaEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ DWORD EntriesRead = 0;
+ LPBYTE Buffer = NULL;
+ DWORD Error = NO_ERROR;
+ // Error = NetApiBufferAllocate( 4096, &Buffer);
+
+ Error = NetRplWkstaEnum( ServerHandle,
+ ProfileName,
+ WkstaEnum->Level,
+ (LPBYTE *)&(WkstaEnum->WkstaInfo.Level0->Buffer),
+ PrefMaxLength,
+ &(WkstaEnum->WkstaInfo.Level0->EntriesRead),
+ TotalEntries,
+ pResumeHandle );
+
+ return( Error);
+}
+
+void __RPC_API MyMIDL_user_free( void __RPC_FAR * pv )
+{
+ NetApiBufferFree( pv );
+}
diff --git a/private/net/svcdlls/rpl/rplinst/rplinst.rc b/private/net/svcdlls/rpl/rplinst/rplinst.rc
new file mode 100644
index 000000000..dbaf0b6f0
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/rplinst.rc
@@ -0,0 +1,14 @@
+/* templated from portuas JonN 1/14/94 */
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RPLINST Utility"
+#define VER_INTERNALNAME_STR "rplinst.exe"
+#define VER_ORIGINALFILENAME_STR "rplinst.exe"
+
+#include "common.ver"
+
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/rpl/rplinst/security.c b/private/net/svcdlls/rpl/rplinst/security.c
new file mode 100644
index 000000000..a064309f9
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/security.c
@@ -0,0 +1,1567 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ security.c
+
+Abstract:
+
+ Security addons to the tree-manipulation module
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+ CODEWORK: Should have more sophisticated error handling
+
+--*/
+
+
+#include "local.h"
+#include "tree.h"
+#include "treei.h"
+#include "security.h"
+#include <ntseapi.h>
+#include <ntrtl.h> // RtlAllocateAndInitializeSid
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <nturtl.h> // see IsNTFS
+
+
+typedef enum _RPL_SD_BLOCK_TYPE {
+
+ RPL_SD_BLOCK_TYPE_ADMINONLY = 0,// Only RPLADMIN gets access
+
+ RPL_SD_BLOCK_TYPE_ALLUSERS, // RPLADMIN gets full access, RPLUSER
+ // gets RX access
+
+ RPL_SD_BLOCK_TYPE_ONEUSER, // RPLADMIN gets full access, one
+ // wksta account gets RWXCDA access
+
+ RPL_SD_BLOCK_TYPE_BADUSER // Skip setting permissions on this user
+} RPL_SD_BLOCK_TYPE;
+
+/*
+ * This is the block of information which is retained between the recursive
+ * calls in a single RplDoTree run. It remembers the SECURITY_DESCRIPTOR
+ * which was assigned to the last file/directory, so if the next
+ * file/directory wants the same permissions (as is usually the case)
+ * we do not have to fetch everything all over again. It also contains
+ * some information about what kind of SECURITY_DESCRIPTOR is currently
+ * stored, plus items needed to get a different SECURITY_DESCRIPTOR.
+ */
+typedef struct _RPL_SD_BLOCK {
+ PSECURITY_DESCRIPTOR psd;
+ RPL_SD_BLOCK_TYPE nChangeableAceType;
+ PSID psidSystem;
+ PSID psidAdministrators;
+ PSID psidSystemOperators;
+ PSID psidRPLUSER;
+ PSID psidGrantTo; // SID of account with name pwszGrantToName
+ PWCHAR pwszGrantToName;
+ // Name of account for BLOCK_TYPE_ONEUSER
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain;
+ SAM_HANDLE hsamAccountDomain;
+} RPL_SD_BLOCK, *PRPL_SD_BLOCK;
+
+//
+// The DACL we assign to files has 3 or 4 ACEs.
+//
+#define RPL_INDEX_SYSTEM_ACE 0
+#define RPL_INDEX_ADMINS_ACE 1
+#define RPL_INDEX_RPLADMIN_ACE 2
+#define RPL_INDEX_CHANGEABLE_ACE 3
+
+
+//
+// Forward declarations
+//
+
+DWORD SetRplPerms(
+ IN PWCHAR pwszPath,
+ IN OUT PVOID * ppv);
+
+DWORD GetSdForFile(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN PWCHAR pwszFile,
+ OUT BOOL * pfSkipPermsOnFile
+ );
+
+DWORD GetSd(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN RPL_SD_BLOCK_TYPE nChangeableAceType,
+ IN PWCHAR pwszGrantTo
+ );
+
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid );
+
+DWORD InitSdBlock(
+ OUT RPL_SD_BLOCK ** ppsdblock
+ );
+
+VOID FreeSdBlock(
+ IN RPL_SD_BLOCK ** ppsdblock
+ );
+
+DWORD GetAccountDomain(
+ OUT SAM_HANDLE * phsamAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ );
+
+DWORD GetAccountSid(
+ IN SAM_HANDLE hsamAccountDomain,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN WCHAR * pwszAccountName,
+ OUT PSID * ppsidAccountSid,
+ IN BOOL fIsLocalgroup,
+ OUT DWORD * pulRid OPTIONAL
+ );
+
+DWORD IsNTFS(
+ IN WCHAR * pszResource,
+ OUT BOOL * pfIsNTFS );
+
+DWORD RplAddWkstaAccount( LPWSTR pszWkstaName,
+ SAM_HANDLE hsamAccountDomain,
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ SAM_HANDLE hsamRPLUSER );
+
+VOID FillUnicodeString( PUNICODE_STRING punistr,
+ LPWSTR lpwsz );
+
+DWORD MyBuildSid( OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN ULONG ulRid );
+
+VOID MyFreeSid( IN OUT PSID * ppsid );
+
+
+/*
+ * Like RplTreeCopy, but also sets permissions on target files/directories.
+ * Target must be in %RplRoot% subtree.
+ *
+ * CODEWORK do something with error buffer
+ */
+DWORD RplTreeCopyAndSetPerms(
+ IN PWCHAR pwszSource,
+ IN PWCHAR pwszTarget )
+{
+ WCHAR Buffer[ 300];
+ DWORD Error = NO_ERROR;
+ PVOID pv = NULL;
+ DWORD Flags = RPL_TREE_COPY | RPL_TREE_AUXILIARY;
+ BOOL fIsNTFS = TRUE;
+
+ //
+ // Check if volume is NTFS, if so never mind about ACLs
+ //
+
+ Error = IsNTFS( pwszTarget, &fIsNTFS );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ } else if (!fIsNTFS) {
+ Flags &= ~RPL_TREE_AUXILIARY;
+ }
+
+ Buffer[ 0] = 0;
+ Error = RplDoTree( pwszSource, pwszTarget,
+ Flags,
+ Buffer, sizeof(Buffer),
+ SetRplPerms, &pv);
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pv != NULL) {
+ FreeSdBlock( (RPL_SD_BLOCK **)(&pv) );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * Sets permissions on a file tree.
+ * Target must be in %RplRoot% subtree.
+ *
+ * CODEWORK do something with error buffer
+ */
+DWORD RplSetPermsOnTree(
+ IN PWCHAR pwszSource )
+{
+ WCHAR Buffer[ 300];
+ DWORD Error = NO_ERROR;
+ PVOID pv = NULL;
+ BOOL fIsNTFS = TRUE;
+
+ //
+ // Check if volume is NTFS, if so do nothing
+ //
+
+ Error = IsNTFS( pwszSource, &fIsNTFS );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ } else if (!fIsNTFS) {
+ goto cleanup;
+ }
+
+ Buffer[ 0] = 0;
+ Error = RplDoTree( pwszSource, NULL,
+ RPL_TREE_AUXILIARY,
+ Buffer, sizeof(Buffer),
+ SetRplPerms, &pv);
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pv != NULL) {
+ FreeSdBlock( (RPL_SD_BLOCK **)(&pv) );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * This is the callback routine called by RplDoTree to set permissions
+ * on a specific file/directory.
+ */
+DWORD SetRplPerms(
+ IN PWCHAR pwszPath,
+ IN OUT PVOID * ppv)
+{
+ DWORD Error = NO_ERROR;
+ RPL_SD_BLOCK ** ppsdblock = (RPL_SD_BLOCK **)ppv;
+ PSECURITY_DESCRIPTOR psd = NULL;
+ BOOL fSkipPermsOnFile = FALSE;
+
+ RPL_ASSERT( pwszPath != NULL );
+ RPL_ASSERT( ppv != NULL );
+
+ Error = GetSdForFile( &psd, ppsdblock, pwszPath, &fSkipPermsOnFile );
+ if (Error != NO_ERROR || fSkipPermsOnFile) {
+ goto cleanup;
+ }
+
+ RPL_ASSERT( psd != NULL );
+
+ //
+ // Set the actual file security. Note that this call will succeed
+ // even if the volume is not NTFS, thus we must check for
+ // partition type elsewhere.
+ //
+
+ if ( !SetFileSecurity( pwszPath, DACL_SECURITY_INFORMATION, psd)) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ return( Error);
+}
+
+
+/*
+ * Get the SECURITY_DESCRIPTOR which is appropriate for a file/directory
+ * at this position in %RplRoot%. First we determine what kind of
+ * SECURITY_DESCRIPTOR we want, then we call GetSd() to build it.
+ */
+DWORD GetSdForFile(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN PWCHAR pwszFile,
+ OUT BOOL * pfSkipPermsOnFile
+ )
+{
+ DWORD Error = NO_ERROR;
+ PWCHAR pwszRelativeFile = NULL;
+ PWCHAR pwszWkstaNameStart = NULL;
+ PWCHAR pwszWkstaNameEnd = NULL;
+ RPL_SD_BLOCK_TYPE blocktype = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ WCHAR awchFile[MAX_PATH];
+ WCHAR wchSave, wchSave2;
+ DWORD cch;
+
+ RPL_ASSERT( ppsd != NULL );
+ RPL_ASSERT( ppsdblock != NULL );
+ RPL_ASSERT( pwszFile != NULL );
+ RPL_ASSERT( pfSkipPermsOnFile != NULL );
+ RPL_ASSERT( lstrlenW(pwszFile) >= (INT)RG_DirectoryLength );
+ RPL_ASSERT( lstrlenW(pwszFile) < MAX_PATH );
+ RPL_ASSERT( RG_DirectoryLength > 0 && RG_DirectoryLength < MAX_PATH );
+
+ *pfSkipPermsOnFile = FALSE;
+
+ //
+ // Make working copy of pwszFile
+ //
+ if (NULL == lstrcpy(awchFile,pwszFile)) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Ensure that awchFile starts with RG_Directory
+ //
+ // RG_Directory always ends with L'\\'. awchFile might be equal to
+ // RG_Directory without the L'\\', we must handle this.
+ //
+ wchSave = awchFile[RG_DirectoryLength];
+ wchSave2 = awchFile[RG_DirectoryLength-1];
+ if (lstrlenW(awchFile) == (int)RG_DirectoryLength-1) {
+ awchFile[RG_DirectoryLength-1] = L'\\';
+ }
+ awchFile[RG_DirectoryLength] = L'\0';
+ if ( 0 != lstrcmpi( awchFile,
+ RG_Directory )) {
+ RPL_ASSERT( FALSE );
+ Error = ERROR_INVALID_PARAMETER;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ awchFile[RG_DirectoryLength] = wchSave;
+ awchFile[RG_DirectoryLength-1] = wchSave2;
+
+ pwszRelativeFile = awchFile + RG_DirectoryLength;
+
+ //
+ // If the path is in rplfiles\binfiles or rplfiles\profiles,
+ // set the changeable ACE to RPLUSER
+ //
+
+ cch = lstrlenW(L"RPLFILES\\BINFILES");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\BINFILES") ) {
+ blocktype = RPL_SD_BLOCK_TYPE_ALLUSERS;
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ cch = lstrlenW(L"RPLFILES\\PROFILES");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\PROFILES") ) {
+ blocktype = RPL_SD_BLOCK_TYPE_ALLUSERS;
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ //
+ // If the path is in rplfiles\machines\* or rplfiles\tmpfiles\*,
+ // set the changeable ACE to the individual workstation account
+ //
+ cch = lstrlenW(L"RPLFILES\\MACHINES\\");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\MACHINES\\") ) {
+ pwszRelativeFile[ cch ] = wchSave;
+ blocktype = RPL_SD_BLOCK_TYPE_ONEUSER;
+ pwszWkstaNameStart = pwszWkstaNameEnd = pwszRelativeFile + cch;
+ while (*pwszWkstaNameEnd != L'\\' && *pwszWkstaNameEnd != L'\0') {
+ pwszWkstaNameEnd++;
+ }
+ *pwszWkstaNameEnd = L'\0';
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+ cch = lstrlenW(L"RPLFILES\\TMPFILES\\");
+ wchSave = pwszRelativeFile[ cch ];
+ pwszRelativeFile[ cch ] = L'\0';
+ if ( 0 == lstrcmpi( pwszRelativeFile, L"RPLFILES\\TMPFILES\\") ) {
+ pwszRelativeFile[ cch ] = wchSave;
+ blocktype = RPL_SD_BLOCK_TYPE_ONEUSER;
+ pwszWkstaNameStart = pwszWkstaNameEnd = pwszRelativeFile + cch;
+ while (*pwszWkstaNameEnd != L'\\' && *pwszWkstaNameEnd != L'\0') {
+ pwszWkstaNameEnd++;
+ }
+ *pwszWkstaNameEnd = L'\0';
+ goto cleanup;
+ }
+ pwszRelativeFile[ cch ] = wchSave;
+
+cleanup:
+
+ //
+ // We now know what kind of SECURITY_DESCRIPTOR we want, so get it.
+ //
+ if (Error == NO_ERROR && !(*pfSkipPermsOnFile)) {
+ Error = GetSd( ppsd, ppsdblock, blocktype, pwszWkstaNameStart );
+ }
+
+ return( Error);
+}
+
+
+/*
+ * This call gets an actual security descriptor which corresponds to
+ * nChangeableAceType and pwszGrantTo. The security descriptor
+ * is only valid until next call to GetSd().
+ *
+ * The caller has to free *ppsdblock eventually, but should not free
+ * *ppsd.
+ */
+DWORD GetSd(
+ OUT PSECURITY_DESCRIPTOR * ppsd,
+ IN OUT RPL_SD_BLOCK ** ppsdblock,
+ IN RPL_SD_BLOCK_TYPE nChangeableAceType,
+ IN PWCHAR pwszGrantTo
+ )
+{
+ DWORD Error = NO_ERROR;
+ PACL paclDacl = NULL;
+ BOOL fDaclPresent = FALSE;
+ BOOL fDaclDefaulted = FALSE;
+
+ RPL_ASSERT( ppsd != NULL);
+ RPL_ASSERT( ppsdblock != NULL);
+
+ //
+ // Create the RPL_SD_BLOCK if it does not yet exist
+ //
+ if ( *ppsdblock == NULL )
+ {
+ Error = InitSdBlock( ppsdblock );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+ RPL_ASSERT( *ppsdblock != NULL );
+
+ //
+ // If the RPL_SD_BLOCK is already configured correctly, return success
+ //
+ if ( ( (*ppsdblock)->nChangeableAceType == nChangeableAceType )
+ && ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_ONEUSER
+ || (0 == lstrcmpW((*ppsdblock)->pwszGrantToName, pwszGrantTo)) )
+ && ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_BADUSER
+ || (0 == lstrcmpW((*ppsdblock)->pwszGrantToName, pwszGrantTo)) )
+ )
+ {
+ goto cleanup;
+ }
+ //
+ // Extract the DACL
+ //
+ if ( !GetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ &fDaclPresent,
+ &paclDacl,
+ &fDaclDefaulted
+ )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( fDaclPresent && !fDaclDefaulted );
+
+ //
+ // Clear the changeable ACE if present
+ //
+ if ( (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_ADMINONLY
+ && (*ppsdblock)->nChangeableAceType != RPL_SD_BLOCK_TYPE_BADUSER )
+ {
+ if ( !DeleteAce( paclDacl, RPL_INDEX_CHANGEABLE_ACE )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we need to decrement ACE count here?
+ (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ }
+
+ //
+ // Clear and rewrite psidGrantTo if necessary
+ //
+ if ( nChangeableAceType == RPL_SD_BLOCK_TYPE_ONEUSER
+ || nChangeableAceType == RPL_SD_BLOCK_TYPE_BADUSER )
+ {
+ if ( (*ppsdblock)->psidGrantTo != NULL) {
+ MyFreeSid( &((*ppsdblock)->psidGrantTo) );
+ TREE_FREE( (*ppsdblock)->pwszGrantToName );
+ (*ppsdblock)->pwszGrantToName = NULL;
+ }
+ if (lstrlenW(pwszGrantTo) > RPL_MAX_WKSTA_NAME_LENGTH)
+ {
+ TREE_ASSERT( ( "Bad user %ws", pwszGrantTo));
+ nChangeableAceType = RPL_SD_BLOCK_TYPE_BADUSER;
+ }
+ else
+ {
+ Error = GetAccountSid( (*ppsdblock)->hsamAccountDomain,
+ (*ppsdblock)->pinfoAccountDomain,
+ pwszGrantTo,
+ &((*ppsdblock)->psidGrantTo),
+ FALSE,
+ NULL );
+ // CODEWORK trap error here?
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ (*ppsdblock)->pwszGrantToName =
+ TREE_ALLOC( (lstrlenW(pwszGrantTo)+1) * sizeof(WCHAR) );
+ if ( (*ppsdblock)->pwszGrantToName == NULL )
+ {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ lstrcpyW( (*ppsdblock)->pwszGrantToName, pwszGrantTo );
+ }
+
+ //
+ // Add changeable ACE if desired
+ //
+ RPL_ASSERT( (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY );
+ switch (nChangeableAceType) {
+ case RPL_SD_BLOCK_TYPE_ADMINONLY:
+ case RPL_SD_BLOCK_TYPE_BADUSER:
+ break;
+ case RPL_SD_BLOCK_TYPE_ALLUSERS:
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_READ | GENERIC_EXECUTE,
+ (*ppsdblock)->psidRPLUSER );
+ break;
+ case RPL_SD_BLOCK_TYPE_ONEUSER:
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_READ
+ | GENERIC_WRITE
+ | GENERIC_EXECUTE
+ | DELETE,
+ (*ppsdblock)->psidGrantTo );
+ break;
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ (*ppsdblock)->nChangeableAceType = nChangeableAceType;
+
+ //
+ // Save the upgraded ACL into the security descriptor
+ //
+ // CODEWORK is this needed?
+ //
+ if ( !SetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ TRUE,
+ paclDacl,
+ FALSE
+ )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error == NO_ERROR) {
+ *ppsd = (*ppsdblock)->psd;
+ }
+
+ return( Error);
+}
+
+
+SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
+
+/*
+ * Create a new RPL_SD_BLOCK
+ */
+DWORD InitSdBlock(
+ OUT RPL_SD_BLOCK ** ppsdblock
+ )
+{
+ DWORD Error = 0;
+ DWORD cbNewDacl = 0;
+ PACL paclDacl = NULL;
+
+ RPL_ASSERT( ppsdblock != NULL );
+ RPL_ASSERT( *ppsdblock == NULL );
+
+ //
+ // Allocate memory for the RPL_SD_BLOCK
+ //
+ *ppsdblock = TREE_ALLOC( sizeof(RPL_SD_BLOCK) );
+ if ( *ppsdblock == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ ZeroMemory( (*ppsdblock), sizeof(RPL_SD_BLOCK) );
+
+ //
+ // Get System SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 1,
+ SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidSystem)
+ );
+ if (Error != NO_ERROR) {
+ goto cleanup;
+ }
+
+ //
+ // Get Administrators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidAdministrators)
+ );
+ if (Error != NO_ERROR) {
+ goto cleanup;
+ }
+
+ //
+ // Get System Operators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_SYSTEM_OPS,
+ 0, 0, 0, 0, 0, 0,
+ &((*ppsdblock)->psidSystemOperators)
+ );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get information about account domain
+ //
+ Error = GetAccountDomain( &((*ppsdblock)->hsamAccountDomain),
+ &((*ppsdblock)->pinfoAccountDomain),
+ GENERIC_EXECUTE );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get RPLUSER SID
+ //
+ Error = GetAccountSid( (*ppsdblock)->hsamAccountDomain,
+ (*ppsdblock)->pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &((*ppsdblock)->psidRPLUSER),
+ TRUE,
+ NULL );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Start assembling DACL
+ //
+ cbNewDacl = sizeof(ACL) + (3*sizeof(ACCESS_ALLOWED_ACE))
+ + (3*GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES))
+ - sizeof(DWORD);
+ paclDacl = (PACL) TREE_ALLOC( cbNewDacl);
+ if ( paclDacl == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ if ( !InitializeAcl( paclDacl, cbNewDacl, ACL_REVISION )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Add System ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidSystem );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we still want to free psidSystem?
+
+ //
+ // Add Administrators ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidAdministrators );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // CODEWORK do we still want to free psidAdministrators?
+
+ //
+ // Add System Operators ACE
+ //
+ Error = AddInheritableAce( paclDacl,
+ GENERIC_ALL,
+ (*ppsdblock)->psidSystemOperators );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ // BUGBUG shouldn't add this for WINNT machines!
+ // CODEWORK do we still want to free psidSystemOperators?
+
+ //
+ // Build security descriptor
+ //
+ (*ppsdblock)->psd = TREE_ALLOC( sizeof(SECURITY_DESCRIPTOR) );
+ if ( (*ppsdblock)->psd == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+ if ( !InitializeSecurityDescriptor( (*ppsdblock)->psd,
+ SECURITY_DESCRIPTOR_REVISION )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ if ( !SetSecurityDescriptorDacl( (*ppsdblock)->psd,
+ TRUE,
+ paclDacl,
+ FALSE )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Set nChangeableAceType in RPL_SD_BLOCK
+ //
+ (*ppsdblock)->nChangeableAceType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+
+cleanup:
+
+ if (Error != NO_ERROR)
+ {
+ FreeSdBlock( ppsdblock );
+
+ if (paclDacl != NULL) {
+ TREE_FREE( paclDacl );
+ paclDacl = NULL;
+ }
+ }
+
+ return(Error);
+}
+
+/*
+ * Add an ACCESS_ALLOWED_ACE to the ACL, with all inheritance flags set
+ */
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid )
+{
+ DWORD Error = NO_ERROR;
+ ACCESS_ALLOWED_ACE * pace = NULL;
+
+ if ( !AddAccessAllowedAce( pacl,
+ ACL_REVISION,
+ mask,
+ psid )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // new ACE should be the last ACE
+ //
+ if ( !GetAce( pacl, (pacl->AceCount) - 1, &pace )) {
+ Error = GetLastError();
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( EqualSid( psid, &(pace->SidStart)) );
+
+ (pace->Header.AceFlags) |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+cleanup:
+
+ return( Error);
+}
+
+
+/*
+ * The RplDoTree run is over, free the RPL_SD_BLOCK
+ */
+VOID FreeSdBlock(
+ IN RPL_SD_BLOCK ** ppsdblock
+ )
+{
+ RPL_ASSERT( ppsdblock != NULL );
+
+ if (*ppsdblock != NULL)
+ {
+ if ((*ppsdblock)->psd != NULL) {
+ TREE_FREE( (*ppsdblock)->psd );
+ }
+ if ((*ppsdblock)->pwszGrantToName != NULL) {
+ TREE_FREE( (*ppsdblock)->pwszGrantToName );
+ }
+ if ((*ppsdblock)->psidSystem != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidSystem );
+ }
+ if ((*ppsdblock)->psidAdministrators != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidAdministrators );
+ }
+ if ((*ppsdblock)->psidSystemOperators != NULL) {
+ RtlFreeSid( (*ppsdblock)->psidSystemOperators);
+ }
+ MyFreeSid( &((*ppsdblock)->psidRPLUSER) );
+ MyFreeSid( &((*ppsdblock)->psidGrantTo) );
+ if ( (*ppsdblock)->pinfoAccountDomain != NULL) {
+ LsaFreeMemory( (*ppsdblock)->pinfoAccountDomain);
+ }
+ if ((*ppsdblock)->hsamAccountDomain != NULL) {
+ SamCloseHandle( (*ppsdblock)->hsamAccountDomain);
+ }
+ }
+ TREE_FREE( *ppsdblock );
+ *ppsdblock = NULL;
+}
+
+
+/*
+ * Get a SAM_HANDLE to the account domain, and other information about
+ * the account domain. This routine uses SAM and LSA directly.
+ */
+DWORD GetAccountDomain(
+ OUT SAM_HANDLE * phsamAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ )
+{
+ DWORD Error = NO_ERROR;
+ SAM_HANDLE hsamServer = NULL;
+ LSA_HANDLE hlsa = NULL;
+ OBJECT_ATTRIBUTES oa;
+ POLICY_ACCOUNT_DOMAIN_INFO * pacctdominfo = NULL;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+
+ RPL_ASSERT( phsamAccountDomain != NULL );
+ RPL_ASSERT( ppinfoAccountDomain != NULL );
+
+ //
+ // Get server handle
+ //
+
+ Error = SamConnect( NULL,
+ &hsamServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Set up object attributes (borrowed from uintlsa.cxx)
+ //
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ //
+ // Get LSA handle
+ //
+
+ Error = LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsa );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get primary domain information
+ //
+
+ Error = LsaQueryInformationPolicy( hlsa,
+ PolicyAccountDomainInformation,
+ ppinfoAccountDomain );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Open account domain handle
+ //
+
+ Error = SamOpenDomain( hsamServer,
+ DesiredAccess,
+ (*ppinfoAccountDomain)->DomainSid,
+ phsamAccountDomain );
+ if ( Error != NERR_Success) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( hsamServer != NULL) {
+ SamCloseHandle( hsamServer);
+ }
+
+ if ( hlsa != NULL) {
+ LsaClose( hlsa);
+ }
+
+ if ( Error != NO_ERROR && (*ppinfoAccountDomain) != NULL) {
+ LsaFreeMemory( (*ppinfoAccountDomain));
+ *ppinfoAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && phsamAccountDomain != NULL) {
+ SamCloseHandle( *phsamAccountDomain);
+ *phsamAccountDomain = NULL;
+ }
+
+ return( Error);
+}
+
+
+/*
+ * Get the SID for an account in the account domain. This method
+ * uses SAM directly to do the lookup, this is why we need to keep
+ * around a SAM domain handle.
+ *
+ * Returns NERR_RplWkstaNeedsUserAcct if the account is not found and
+ * !fIsLocalgroup, NERR_RplNeedsRPLUSERAcct if fIsLocalgroup
+ *
+ * Use MyFreeSid to free the SID returned in **ppsidAccountSid
+ */
+DWORD GetAccountSid(
+ IN SAM_HANDLE hsamAccountDomain,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN WCHAR * pwszAccountName,
+ OUT PSID * ppsidAccountSid,
+ IN BOOL fIsLocalgroup,
+ OUT DWORD * pulRid OPTIONAL
+ )
+{
+ DWORD Error = NO_ERROR;
+ UNICODE_STRING unistr;
+ PULONG RelativeIds = NULL;
+ PSID_NAME_USE Use = NULL;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ RPL_ASSERT( hsamAccountDomain != NULL );
+ RPL_ASSERT( pinfoAccountDomain != NULL );
+ RPL_ASSERT( pwszAccountName != NULL );
+ RPL_ASSERT( ppsidAccountSid != NULL && *ppsidAccountSid == NULL );
+
+ //
+ // Lookup name
+ //
+
+ FillUnicodeString( &unistr, pwszAccountName );
+
+ Error = SamLookupNamesInDomain( hsamAccountDomain,
+ 1,
+ &unistr,
+ &RelativeIds,
+ &Use );
+ if (Error != NO_ERROR) {
+ if (Error == STATUS_NONE_MAPPED) {
+ Error = (fIsLocalgroup) ? NERR_RplNeedsRPLUSERAcct
+ : NERR_RplWkstaNeedsUserAcct;
+ }
+ // TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( RelativeIds != NULL && Use != NULL );
+
+ if ( Use[0] != ((fIsLocalgroup) ? SidTypeAlias : SidTypeUser) ) {
+ Error = (fIsLocalgroup) ? NERR_RplNeedsRPLUSERAcct
+ : NERR_RplWkstaNeedsUserAcct;
+ goto cleanup;
+ }
+
+ //
+ // Construct account SID from RID and domain SID
+ //
+
+ Error = MyBuildSid( ppsidAccountSid, pinfoAccountDomain, RelativeIds[0] );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( Error == NO_ERROR && pulRid != NULL) {
+ *pulRid = RelativeIds[0];
+ }
+
+ if (RelativeIds != NULL) {
+ SamFreeMemory( RelativeIds);
+ }
+ if (Use != NULL) {
+ SamFreeMemory( Use);
+ }
+ if ( Error != NO_ERROR ) {
+ MyFreeSid( ppsidAccountSid );
+ }
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: IsNTFS
+
+ SYNOPSIS: This function checks the given file resource and attempts to
+ determine if it is on an NTFS partition.
+
+ ENTRY: pwszResource - Pointer to file/directory name in the form
+ "X:\aaa\bbb\ccc
+ pfIsNTFS - Pointer to BOOL that will receive the results
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 23-Feb-1994 Copied from acledit\ntfsacl.cxx
+
+********************************************************************/
+
+DWORD IsNTFS(
+ IN WCHAR * pwszResource,
+ OUT BOOL * pfIsNTFS )
+{
+ DWORD Error = NO_ERROR ;
+ HANDLE hDrive = NULL ;
+ WCHAR awchDosDriveName[4];
+ WCHAR awchNtDriveName[40];
+ UNICODE_STRING unistrNtDriveName;
+ BYTE buffFsInfo[ sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 200 ] ;
+ PFILE_FS_ATTRIBUTE_INFORMATION FsInfo =
+ (PFILE_FS_ATTRIBUTE_INFORMATION)buffFsInfo ;
+ OBJECT_ATTRIBUTES oa ;
+ IO_STATUS_BLOCK StatusBlock ;
+
+ RPL_ASSERT( pwszResource != NULL
+ && lstrlen(pwszResource) >= 3
+ && pfIsNTFS != NULL ) ;
+
+ *pfIsNTFS = FALSE ;
+
+ //
+ // Determine the NT drive name
+ //
+
+ awchDosDriveName[0] = pwszResource[0];
+ awchDosDriveName[1] = pwszResource[1];
+ awchDosDriveName[2] = pwszResource[2];
+ awchDosDriveName[3] = L'\0';
+ RPL_ASSERT( awchDosDriveName[1] == L':'
+ && awchDosDriveName[2] == L'\\' );
+
+ unistrNtDriveName.Buffer = awchNtDriveName;
+ unistrNtDriveName.Length = sizeof(awchNtDriveName);
+ unistrNtDriveName.MaximumLength = sizeof(awchNtDriveName);
+
+ if (!RtlDosPathNameToNtPathName_U( awchDosDriveName,
+ &unistrNtDriveName,
+ NULL,
+ NULL))
+ {
+ RPL_ASSERT( FALSE ) ;
+ Error = ERROR_NOT_ENOUGH_MEMORY ;
+ goto cleanup;
+ }
+ RPL_ASSERT( unistrNtDriveName.Length < unistrNtDriveName.MaximumLength );
+ unistrNtDriveName.Buffer[unistrNtDriveName.Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Open the NT volume and query volume information
+ //
+
+ InitializeObjectAttributes( &oa,
+ &unistrNtDriveName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ 0 );
+ Error = NtOpenFile( &hDrive,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &oa,
+ &StatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_ALERT);
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = NtQueryVolumeInformationFile(
+ hDrive,
+ &StatusBlock,
+ (PVOID) FsInfo,
+ sizeof(buffFsInfo),
+ FileFsAttributeInformation );
+ if (Error != NO_ERROR) {
+ if (Error == ERROR_ACCESS_DENIED) {
+ TREE_ASSERT( (
+"IsNTFS - Unable to determine volume information (access denied) assuming the file system is NTFS"
+ ) ) ;
+ Error = NO_ERROR ;
+ *pfIsNTFS = TRUE ;
+ } else {
+ TREE_ASSERT( ( "Error=%d", Error));
+ }
+ goto cleanup;
+ }
+
+ if ( FsInfo->FileSystemAttributes & FILE_PERSISTENT_ACLS )
+ {
+ *pfIsNTFS = TRUE ;
+ }
+
+cleanup:
+
+ /* Close the volume if we ever opened it
+ */
+ if ( hDrive != NULL )
+ {
+ NtClose( hDrive );
+ }
+
+ return( Error) ;
+}
+
+
+/*******************************************************************
+
+ NAME: RplCreateWkstaAccounts
+
+ SYNOPSIS: This function checks whether a user account exists for
+ each RPL workstation, and creates one if not. It also
+ adds all of these accounts to the RPLUSER local group.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 04-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplCheckWkstaAccounts( RPL_RPC_HANDLE ServerHandle )
+{
+ DWORD Error = 0;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain = NULL;
+ SAM_HANDLE hsamAccountDomain = NULL;
+ PSID psidRPLUSER = NULL;
+ ULONG ulRidRPLUSER = 0;
+ SAM_HANDLE hsamRPLUSER = NULL;
+ DWORD ErrorEnum = NO_ERROR;
+ RPL_WKSTA_ENUM rplwkstaenum;
+ RPL_WKSTA_INFO_0_CONTAINER rplwksta0container;
+ DWORD cEntries = 0;
+ DWORD hResumeHandle = 0;
+ DWORD i;
+
+ rplwkstaenum.Level = 0;
+ rplwkstaenum.WkstaInfo.Level0 = &rplwksta0container;
+ rplwkstaenum.WkstaInfo.Level0->EntriesRead = 0;
+ rplwkstaenum.WkstaInfo.Level0->Buffer = NULL;
+
+ Error = GetAccountDomain( &hsamAccountDomain,
+ &pinfoAccountDomain,
+ GENERIC_EXECUTE | DOMAIN_CREATE_USER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &psidRPLUSER,
+ TRUE,
+ &ulRidRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = SamOpenAlias( hsamAccountDomain,
+ ALIAS_ADD_MEMBER,
+ ulRidRPLUSER,
+ &hsamRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ do {
+ ErrorEnum = NetrRplWkstaEnum( ServerHandle,
+ NULL,
+ &rplwkstaenum,
+ (ULONG)-1,
+ &cEntries,
+ &hResumeHandle );
+ switch (ErrorEnum) {
+ case STATUS_MORE_ENTRIES:
+ case ERROR_MORE_DATA:
+ case NO_ERROR:
+ case STATUS_NO_MORE_ENTRIES:
+
+ if (rplwkstaenum.WkstaInfo.Level0->Buffer == NULL) {
+ // no workstations
+ break;
+ }
+ for ( i = 0;
+ (Error == NO_ERROR)
+ && (i < rplwkstaenum.WkstaInfo.Level0->EntriesRead);
+ i++ ) {
+ Error = RplAddWkstaAccount(
+ rplwkstaenum.WkstaInfo.Level0->Buffer[i].WkstaName,
+ hsamAccountDomain,
+ pinfoAccountDomain,
+ hsamRPLUSER );
+ }
+
+ MIDL_user_free( rplwkstaenum.WkstaInfo.Level0->Buffer );
+ rplwkstaenum.WkstaInfo.Level0->Buffer = NULL;
+ break;
+
+ default:
+ Error = ErrorEnum;
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+ } while ( Error == STATUS_MORE_ENTRIES || Error == ERROR_MORE_DATA );
+
+cleanup:
+
+ /*
+ * We have no way to free the resume handle
+ */
+
+ if ( rplwkstaenum.WkstaInfo.Level0->Buffer != NULL ) {
+ MIDL_user_free( rplwkstaenum.WkstaInfo.Level0->Buffer );
+ }
+
+ if ( pinfoAccountDomain != NULL) {
+ LsaFreeMemory( pinfoAccountDomain);
+ }
+ if (hsamAccountDomain != NULL) {
+ SamCloseHandle( hsamAccountDomain);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ MyFreeSid( &psidRPLUSER );
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: RplAddWkstaAccount
+
+ SYNOPSIS: This function checks whether a user account exists for
+ a single workstation, and creates one if not. It also
+ adds the account to the RPLUSER local group.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 04-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplAddWkstaAccount( LPWSTR pszWkstaName,
+ SAM_HANDLE hsamAccountDomain,
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ SAM_HANDLE hsamRPLUSER )
+{
+ DWORD Error = 0;
+ UNICODE_STRING unistr;
+ PSID psidUser = NULL;
+ ULONG ulRidUser = 0;
+ SAM_HANDLE hsamUser = NULL;
+
+ RPL_ASSERT( pszWkstaName != NULL
+ && hsamAccountDomain != NULL
+ && pinfoAccountDomain != NULL
+ && hsamRPLUSER != NULL );
+
+ /*
+ * Get SID of existing account. It would be more efficient to look
+ * up all the accounts at once.
+ */
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ pszWkstaName,
+ &psidUser,
+ FALSE,
+ &ulRidUser );
+ if ( Error == NERR_RplWkstaNeedsUserAcct )
+ {
+ /*
+ * Account doesn't exist, add it
+ *
+ * Note that this will fail unless user is administrator or
+ * at least account operator
+ */
+
+ FillUnicodeString( &unistr, pszWkstaName );
+
+ Error = SamCreateUserInDomain( hsamAccountDomain,
+ &unistr,
+ 0x0,
+ &hsamUser,
+ &ulRidUser );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = MyBuildSid( &psidUser, pinfoAccountDomain, ulRidUser );
+ if (Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ }
+
+ /*
+ * Add account to RPLUSER alias
+ *
+ * Note that this will fail unless user is administrator or
+ * at least account operator
+ */
+
+ Error = SamAddMemberToAlias( hsamRPLUSER, psidUser );
+ switch (Error) {
+ case STATUS_MEMBER_IN_ALIAS:
+ Error = NO_ERROR;
+ // fall through
+ case NO_ERROR:
+ break;
+ default:
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+
+cleanup:
+
+ MyFreeSid( &psidUser );
+
+ if (hsamUser != NULL) {
+ SamCloseHandle( hsamUser);
+ }
+
+ return( Error);
+}
+
+
+/*******************************************************************
+
+ NAME: RplAddRPLUSERGroup
+
+ SYNOPSIS: This function checks whether the RPLUSER local group
+ exists, and creates it if it doesn't.
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 03-Mar-1994 Created
+
+********************************************************************/
+
+DWORD RplAddRPLUSERGroup( VOID )
+{
+ DWORD Error = 0;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain = NULL;
+ SAM_HANDLE hsamAccountDomain = NULL;
+ PSID psidRPLUSER = NULL;
+ UNICODE_STRING unistr;
+ SAM_HANDLE hsamRPLUSER = NULL;
+ ULONG ulRidRPLUSER = 0;
+
+ Error = GetAccountDomain( &hsamAccountDomain,
+ &pinfoAccountDomain,
+ GENERIC_EXECUTE | DOMAIN_CREATE_ALIAS );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = GetAccountSid( hsamAccountDomain,
+ pinfoAccountDomain,
+ RPL_GROUP_RPLUSER,
+ &psidRPLUSER,
+ TRUE,
+ NULL );
+ if ( Error == NO_ERROR) {
+ // RPLUSER already exists
+ goto cleanup;
+ } else if (Error != NERR_RplNeedsRPLUSERAcct)
+ {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ FillUnicodeString( &unistr, RPL_GROUP_RPLUSER );
+
+ Error = SamCreateAliasInDomain( hsamAccountDomain,
+ &unistr,
+ 0x0,
+ &hsamRPLUSER,
+ &ulRidRPLUSER );
+ if ( Error != NO_ERROR) {
+ TREE_ASSERT( ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if ( pinfoAccountDomain != NULL) {
+ LsaFreeMemory( pinfoAccountDomain);
+ }
+ if (hsamAccountDomain != NULL) {
+ SamCloseHandle( hsamAccountDomain);
+ }
+
+ if (hsamRPLUSER != NULL) {
+ SamCloseHandle( hsamRPLUSER);
+ }
+
+ MyFreeSid( &psidRPLUSER );
+
+ return( Error);
+}
+
+VOID FillUnicodeString( PUNICODE_STRING punistr,
+ LPWSTR lpwsz )
+{
+ ASSERT( punistr != NULL && lpwsz == NULL );
+ punistr->Buffer = lpwsz;
+ punistr->Length = lstrlenW(lpwsz) * sizeof(WCHAR);
+ punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
+}
+
+DWORD MyBuildSid( OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN ULONG ulRid )
+{
+ DWORD Error = 0;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ ASSERT( ppsidAccountSid != NULL && pinfoAccountDomain != NULL && ulRid != 0 );
+
+ cbNewSid = GetLengthSid(pinfoAccountDomain->DomainSid) + sizeof(DWORD);
+ *ppsidAccountSid = TREE_ALLOC( cbNewSid );
+ if ( *ppsidAccountSid == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+
+ if ( !CopySid( cbNewSid, *ppsidAccountSid, pinfoAccountDomain->DomainSid)) {
+ Error = GetLastError();
+ goto cleanup;
+ }
+
+ pcSubAuthorityCount = GetSidSubAuthorityCount( *ppsidAccountSid );
+ RPL_ASSERT( pcSubAuthorityCount != NULL );
+ (*pcSubAuthorityCount)++;
+ pdwLastSubAuthority = GetSidSubAuthority(
+ *ppsidAccountSid, (DWORD)((*pcSubAuthorityCount)-1) );
+ RPL_ASSERT( pdwLastSubAuthority != NULL );
+ *pdwLastSubAuthority = ulRid;
+
+cleanup:
+
+ if (Error != NO_ERROR) {
+ MyFreeSid( ppsidAccountSid );
+ }
+
+ return( Error);
+}
+
+VOID MyFreeSid( IN OUT PSID * ppsid )
+{
+ RPL_ASSERT( ppsid != NULL );
+
+ if ( *ppsid != NULL) {
+ TREE_FREE( *ppsid );
+ *ppsid = NULL;
+ }
+}
diff --git a/private/net/svcdlls/rpl/rplinst/security.h b/private/net/svcdlls/rpl/rplinst/security.h
new file mode 100644
index 000000000..403aa7fd1
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/security.h
@@ -0,0 +1,17 @@
+//
+// #define RPL_RPC_HANDLE RPL_HANDLE
+//
+
+#define NetrRplWkstaEnum MyNetrRplWkstaEnum
+#define MIDL_user_free MyMIDL_user_free
+
+#include <rplsvc_s.h> // RPL_WKSTA_ENUM
+
+#include "..\server\security.h"
+
+#undef TREE_ALLOC
+#undef TREE_FREE
+#undef TREE_ASSERT
+#define TREE_ALLOC(x) (malloc( (x) ))
+#define TREE_FREE(x) (free( (x) ))
+#define TREE_ASSERT( x ) ASSERT(FALSE)
diff --git a/private/net/svcdlls/rpl/rplinst/sources b/private/net/svcdlls/rpl/rplinst/sources
new file mode 100644
index 000000000..60cf3e52f
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/sources
@@ -0,0 +1,38 @@
+MAJORCOMP=net
+MINORCOMP=rplinst
+
+TARGETPATH=obj
+TARGETNAME=rplinst
+TARGETTYPE=PROGRAM
+NTTARGETFILE0=nlstxt.h nlstxt.mc nlstxt.rc
+
+TARGETLIBS= \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\server;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplinst.rc \
+ rplinst.c \
+ security.c \
+ tree.c \
+ debug.c \
+ querydir.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplinst/tree.c b/private/net/svcdlls/rpl/rplinst/tree.c
new file mode 100644
index 000000000..08b3a942c
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/tree.c
@@ -0,0 +1,2 @@
+
+#include "..\server\tree.c"
diff --git a/private/net/svcdlls/rpl/rplinst/tree.h b/private/net/svcdlls/rpl/rplinst/tree.h
new file mode 100644
index 000000000..d68d34917
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/tree.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Exports from tree.c
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+
+--*/
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+
+
diff --git a/private/net/svcdlls/rpl/rplinst/treei.h b/private/net/svcdlls/rpl/rplinst/treei.h
new file mode 100644
index 000000000..3489cd759
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplinst/treei.h
@@ -0,0 +1,51 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Internal definitions from tree.c
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+--*/
+
+typedef DWORD (RPL_TREE_CALLBACK)( PWCHAR pszPath, PVOID * ppv);
+
+#define RPL_TREE_COPY_DACL 0x1 // not yet implemented
+#define RPL_TREE_COPY_SACL 0x2 // not yet implemented
+#define RPL_TREE_AUXILIARY 0x2000 // perform callback action
+#define RPL_TREE_COPY 0x4000
+#define RPL_TREE_DELETE 0x8000
+
+//
+// Forward declarations
+//
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ PWCHAR Buffer,
+ DWORD BufferSize,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PVOID AuxiliaryBlock
+ );
+
+VOID LoadError(
+ PWCHAR FilePath,
+ LPVOID Buffer,
+ DWORD BufferSize
+ );
+
diff --git a/private/net/svcdlls/rpl/rplstart/local.h b/private/net/svcdlls/rpl/rplstart/local.h
new file mode 100644
index 000000000..85977e516
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/local.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for RPLSTART
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Revision History:
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+
+//
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+
+#include <rpldb.h>
+
+#include "rplmgrd.h"
+
+#define RPL_BUGBUG_ERROR ((DWORD)-1) // need to get rid of these
diff --git a/private/net/svcdlls/rpl/rplstart/makefile b/private/net/svcdlls/rpl/rplstart/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/rplstart/rplstart.c b/private/net/svcdlls/rpl/rplstart/rplstart.c
new file mode 100644
index 000000000..1903c25a5
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/rplstart.c
@@ -0,0 +1,139 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplstart.c
+
+Abstract:
+
+ Copies fresh RPL database and files.
+
+ BUGBUG Should polish and make WIN32 application out of this.
+
+Author:
+
+ Jon Newman (jonn) 12 - January - 1994
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+
+#define RPL_INSTALL_FILES L"\\INSTALL"
+
+
+DWORD _CRTAPI1 main ( VOID)
+{
+
+ WCHAR awchConvertPath[ PATHLEN+1];
+ DWORD dwErr;
+
+ dwErr = I_NetRpl_QueryDirectory( awchConvertPath, NULL);
+ if ( dwErr != NO_ERROR) {
+ return( dwErr);
+ }
+
+ if ( lstrlenW(awchConvertPath) + lstrlenW(RPL_INSTALL_FILES) > PATHLEN) {
+ return( RPL_BUGBUG_ERROR);
+ }
+
+ (void) lstrcatW( awchConvertPath, RPL_INSTALL_FILES);
+
+ dwErr = I_NetRplCmd_ConvertDatabase( awchConvertPath);
+
+// BUGBUG proper error reporting
+// BUGBUG print final message
+
+ return( dwErr);
+
+#if 0
+
+ DWORD Error;
+ JET_ERR JetError;
+
+ Assert = 0;
+#ifdef __JET500
+ CallM( JetSetSystemParameter( 0, JET_paramSystemPath, 0, "."));
+#else
+ CallM( JetSetSystemParameter( 0, JET_paramSysDbPath, 0, "system.mdb"));
+#endif
+ CallM( JetSetSystemParameter( 0, JET_paramTempPath, 0, "temp.tmp"));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxBuffers, 250, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxOpenTables, 30, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxOpenTableIndexes, 105, NULL))
+ CallM( JetSetSystemParameter( 0, JET_paramMaxCursors, 100, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxSessions, 10, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxVerPages, 64, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramLogFilePath, 0, "."));
+ CallM( JetSetSystemParameter( 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallM( JetSetSystemParameter( 0, JET_paramLogFileSize, 1000, NULL));
+ CallM( JetSetSystemParameter( 0, JET_paramBaseName, "j50", NULL));
+#else
+ CallM( JetSetSystemParameter( 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallM( JetSetSystemParameter( 0, JET_paramLogFlushThreshold, 10, NULL));
+
+ CallM( JetInit());
+ CallM( JetBeginSession( &SesId, "admin", ""));
+ JetError = JetCreateDatabase( SesId, RPL_SERVICE_DATABASE, NULL, &DbId, JET_bitDbSingleExclusive);
+
+ if ( JetError == JET_errDatabaseDuplicate) {
+ CallM( JetAttachDatabase( SesId, RPL_SERVICE_DATABASE, 0));
+ CallM( JetOpenDatabase( SesId, RPL_SERVICE_DATABASE, NULL, &DbId, JET_bitDbExclusive));
+ BootListTable();
+ ConfigListTable();
+ ProfileListTable();
+ WkstaListTable();
+ ListWkstaInfo( L"02608C1B87A5");
+ } else if ( JetError == JET_errSuccess) {
+ Error = ProcessRplMap();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+ Error = ProcessRplmgrIni();
+ if ( Error != ERROR_SUCCESS) {
+ return( Error);
+ }
+ ConfigPruneTable();
+ ProfilePruneTable();
+ WkstaPruneTable();
+ //
+ // If we were to return here, database would be left in an unusable
+ // state and any further app calling JetInit() for this database
+ // would fail.
+ //
+ BootCloseTable();
+ ConfigCloseTable();
+ ProfileCloseTable();
+ WkstaCloseTable();
+ AdapterCloseTable();
+ } else {
+ RplDump( ++Assert, ("CreateDatabase: JetError=%d", JetError));
+ CallM( JetEndSession( SesId, 0));
+ CallM( JetTerm2( BUGBUG, JET_bitTermComplete));
+ return( MapJetError( JetError));
+ }
+
+ CallM( JetCloseDatabase( SesId, DbId, 0));
+ CallM( JetDetachDatabase( SesId, RPL_SERVICE_DATABASE));
+ CallM( JetEndSession( SesId, 0));
+ CallM( JetTerm2( BUGBUG, JET_bitTermComplete));
+
+#endif
+
+ return( ERROR_SUCCESS);
+}
+
diff --git a/private/net/svcdlls/rpl/rplstart/sources b/private/net/svcdlls/rpl/rplstart/sources
new file mode 100644
index 000000000..5f4748061
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplstart/sources
@@ -0,0 +1,32 @@
+MAJORCOMP=net
+MINORCOMP=rplstart
+
+TARGETPATH=obj
+TARGETNAME=rplstart
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\obj\*\jet.lib \
+ ..\lib\obj\*\rpllib.lib \
+ ..\convert\obj\*\rplmgrd.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ rplstart.c
+
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_RPLCNV
+
+UMTYPE=console
+
diff --git a/private/net/svcdlls/rpl/rplsvc.idl b/private/net/svcdlls/rpl/rplsvc.idl
new file mode 100644
index 000000000..aa8b41d8b
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc.idl
@@ -0,0 +1,543 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the
+ remotable NetRpl APIs that reside in the messenger service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 27-July-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+ 27-July-1993 vladimv
+ Created
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(28607FF1-15A0-8E03-D670-B89EEC8EB047),
+ version(1.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface rplsvc
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+#include <lmcons.h>
+
+//
+// BUGBUG - take this definition out when midl understands LPWSTR etc
+//
+
+#ifdef UNICODE
+#define LPTSTR wchar_t*
+#endif
+
+//
+// Generic Handle used to bind from client to server.
+//
+
+typedef [handle] LPTSTR RPL_NAME;
+typedef RPL_NAME * PRPL_NAME;
+typedef PRPL_NAME LPRPL_NAME;
+
+//
+// Context Handle (internal definition)
+//
+
+typedef [context_handle] PVOID RPL_RPC_HANDLE;
+typedef RPL_RPC_HANDLE * PRPL_RPC_HANDLE;
+typedef PRPL_RPC_HANDLE LPRPL_RPC_HANDLE;
+
+
+//
+// Data Structures
+//
+
+//
+// SERVICE data - used by Get & Set
+//
+
+typedef [switch_type(DWORD)] union _RPL_INFO_STRUCT {
+ [case(0)] LPRPL_INFO_0 RplInfo0;
+ [case(1)] LPRPL_INFO_1 RplInfo1;
+} RPL_INFO_STRUCT, *PRPL_INFO_STRUCT, *LPRPL_INFO_STRUCT;
+
+
+//
+// BOOT data - used by Enum
+//
+
+typedef struct _RPL_BOOT_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_0 Buffer;
+} RPL_BOOT_INFO_0_CONTAINER, *PRPL_BOOT_INFO_0_CONTAINER, *LPRPL_BOOT_INFO_0_CONTAINER;
+
+typedef struct _RPL_BOOT_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_1 Buffer;
+} RPL_BOOT_INFO_1_CONTAINER, *PRPL_BOOT_INFO_1_CONTAINER, *LPRPL_BOOT_INFO_1_CONTAINER;
+
+typedef struct _RPL_BOOT_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_BOOT_INFO_2 Buffer;
+} RPL_BOOT_INFO_2_CONTAINER, *PRPL_BOOT_INFO_2_CONTAINER, *LPRPL_BOOT_INFO_2_CONTAINER;
+
+typedef struct _RPL_BOOT_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_BOOT_ENUM_UNION {
+ [case(0)] LPRPL_BOOT_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_BOOT_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_BOOT_INFO_2_CONTAINER Level2;
+ } BootInfo;
+} RPL_BOOT_ENUM, *PRPL_BOOT_ENUM, *LPRPL_BOOT_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_BOOT_INFO_STRUCT {
+ [case(0)] LPRPL_BOOT_INFO_0 BootInfo0;
+ [case(1)] LPRPL_BOOT_INFO_1 BootInfo1;
+ [case(2)] LPRPL_BOOT_INFO_2 BootInfo2;
+} RPL_BOOT_INFO_STRUCT, *PRPL_BOOT_INFO_STRUCT, *LPRPL_BOOT_INFO_STRUCT;
+
+
+
+//
+// CONFIG data - used by Enum
+//
+
+typedef struct _RPL_CONFIG_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_0 Buffer;
+} RPL_CONFIG_INFO_0_CONTAINER, *PRPL_CONFIG_INFO_0_CONTAINER, *LPRPL_CONFIG_INFO_0_CONTAINER;
+
+typedef struct _RPL_CONFIG_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_1 Buffer;
+} RPL_CONFIG_INFO_1_CONTAINER, *PRPL_CONFIG_INFO_1_CONTAINER, *LPRPL_CONFIG_INFO_1_CONTAINER;
+
+typedef struct _RPL_CONFIG_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_CONFIG_INFO_2 Buffer;
+} RPL_CONFIG_INFO_2_CONTAINER, *PRPL_CONFIG_INFO_2_CONTAINER, *LPRPL_CONFIG_INFO_2_CONTAINER;
+
+typedef struct _RPL_CONFIG_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_CONFIG_ENUM_UNION {
+ [case(0)] LPRPL_CONFIG_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_CONFIG_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_CONFIG_INFO_2_CONTAINER Level2;
+ } ConfigInfo;
+} RPL_CONFIG_ENUM, *PRPL_CONFIG_ENUM, *LPRPL_CONFIG_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_CONFIG_INFO_STRUCT {
+ [case(0)] LPRPL_CONFIG_INFO_0 ConfigInfo0;
+ [case(1)] LPRPL_CONFIG_INFO_1 ConfigInfo1;
+ [case(2)] LPRPL_CONFIG_INFO_2 ConfigInfo2;
+} RPL_CONFIG_INFO_STRUCT, *PRPL_CONFIG_INFO_STRUCT, *LPRPL_CONFIG_INFO_STRUCT;
+
+
+
+//
+// PROFILE data - used by Enum, Get, Set & Add
+//
+
+typedef struct _RPL_PROFILE_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_0 Buffer;
+} RPL_PROFILE_INFO_0_CONTAINER, *PRPL_PROFILE_INFO_0_CONTAINER, *LPRPL_PROFILE_INFO_0_CONTAINER;
+
+typedef struct _RPL_PROFILE_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_1 Buffer;
+} RPL_PROFILE_INFO_1_CONTAINER, *PRPL_PROFILE_INFO_1_CONTAINER, *LPRPL_PROFILE_INFO_1_CONTAINER;
+
+typedef struct _RPL_PROFILE_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_PROFILE_INFO_2 Buffer;
+} RPL_PROFILE_INFO_2_CONTAINER, *PRPL_PROFILE_INFO_2_CONTAINER, *LPRPL_PROFILE_INFO_2_CONTAINER;
+
+
+typedef struct _RPL_PROFILE_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_PROFILE_ENUM_UNION {
+ [case(0)] LPRPL_PROFILE_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_PROFILE_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_PROFILE_INFO_2_CONTAINER Level2;
+ } ProfileInfo;
+} RPL_PROFILE_ENUM, *PRPL_PROFILE_ENUM, *LPRPL_PROFILE_ENUM;
+
+
+typedef [switch_type(DWORD)] union _RPL_PROFILE_INFO_STRUCT {
+ [case(0)] LPRPL_PROFILE_INFO_0 ProfileInfo0;
+ [case(1)] LPRPL_PROFILE_INFO_1 ProfileInfo1;
+ [case(2)] LPRPL_PROFILE_INFO_2 ProfileInfo2;
+} RPL_PROFILE_INFO_STRUCT, *PRPL_PROFILE_INFO_STRUCT, *LPRPL_PROFILE_INFO_STRUCT, PRPL_PROFILE_INFO, LPRPL_PROFILE_INFO;
+
+
+//
+// VENDOR data - used by Enum
+//
+
+typedef struct _RPL_VENDOR_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_VENDOR_INFO_0 Buffer;
+} RPL_VENDOR_INFO_0_CONTAINER, *PRPL_VENDOR_INFO_0_CONTAINER, *LPRPL_VENDOR_INFO_0_CONTAINER;
+
+typedef struct _RPL_VENDOR_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_VENDOR_INFO_1 Buffer;
+} RPL_VENDOR_INFO_1_CONTAINER, *PRPL_VENDOR_INFO_1_CONTAINER, *LPRPL_VENDOR_INFO_1_CONTAINER;
+
+typedef struct _RPL_VENDOR_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_VENDOR_ENUM_UNION {
+ [case(0)] LPRPL_VENDOR_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_VENDOR_INFO_1_CONTAINER Level1;
+ } VendorInfo;
+} RPL_VENDOR_ENUM, *PRPL_VENDOR_ENUM, *LPRPL_VENDOR_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_VENDOR_INFO_STRUCT {
+ [case(0)] LPRPL_VENDOR_INFO_0 VendorInfo0;
+ [case(1)] LPRPL_VENDOR_INFO_1 VendorInfo1;
+} RPL_VENDOR_INFO_STRUCT, *PRPL_VENDOR_INFO_STRUCT, *LPRPL_VENDOR_INFO_STRUCT;
+
+
+//
+// ADAPTER data - used by Enum
+//
+
+typedef struct _RPL_ADAPTER_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_ADAPTER_INFO_0 Buffer;
+} RPL_ADAPTER_INFO_0_CONTAINER, *PRPL_ADAPTER_INFO_0_CONTAINER, *LPRPL_ADAPTER_INFO_0_CONTAINER;
+
+typedef struct _RPL_ADAPTER_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_ADAPTER_INFO_1 Buffer;
+} RPL_ADAPTER_INFO_1_CONTAINER, *PRPL_ADAPTER_INFO_1_CONTAINER, *LPRPL_ADAPTER_INFO_1_CONTAINER;
+
+typedef struct _RPL_ADAPTER_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_ADAPTER_ENUM_UNION {
+ [case(0)] LPRPL_ADAPTER_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_ADAPTER_INFO_1_CONTAINER Level1;
+ } AdapterInfo;
+} RPL_ADAPTER_ENUM, *PRPL_ADAPTER_ENUM, *LPRPL_ADAPTER_ENUM;
+
+typedef [switch_type(DWORD)] union _RPL_ADAPTER_INFO_STRUCT {
+ [case(0)] LPRPL_ADAPTER_INFO_0 AdapterInfo0;
+ [case(1)] LPRPL_ADAPTER_INFO_1 AdapterInfo1;
+} RPL_ADAPTER_INFO_STRUCT, *PRPL_ADAPTER_INFO_STRUCT, *LPRPL_ADAPTER_INFO_STRUCT;
+
+
+//
+// WKSTA data - used by Enum, Get, Set & Add
+//
+
+typedef struct _RPL_WKSTA_INFO_0_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_0 Buffer;
+} RPL_WKSTA_INFO_0_CONTAINER, *PRPL_WKSTA_INFO_0_CONTAINER, *LPRPL_WKSTA_INFO_0_CONTAINER;
+
+typedef struct _RPL_WKSTA_INFO_1_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_1 Buffer;
+} RPL_WKSTA_INFO_1_CONTAINER, *PRPL_WKSTA_INFO_1_CONTAINER, *LPRPL_WKSTA_INFO_1_CONTAINER;
+
+typedef struct _RPL_WKSTA_INFO_2_CONTAINER {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] LPRPL_WKSTA_INFO_2 Buffer;
+} RPL_WKSTA_INFO_2_CONTAINER, *PRPL_WKSTA_INFO_2_CONTAINER, *LPRPL_WKSTA_INFO_2_CONTAINER;
+
+
+typedef struct _RPL_WKSTA_ENUM {
+ DWORD Level;
+ [switch_is(Level)] union _RPL_WKSTA_ENUM_UNION {
+ [case(0)] LPRPL_WKSTA_INFO_0_CONTAINER Level0;
+ [case(1)] LPRPL_WKSTA_INFO_1_CONTAINER Level1;
+ [case(2)] LPRPL_WKSTA_INFO_2_CONTAINER Level2;
+ } WkstaInfo;
+} RPL_WKSTA_ENUM, *PRPL_WKSTA_ENUM, *LPRPL_WKSTA_ENUM;
+
+
+typedef [switch_type(DWORD)] union _RPL_WKSTA_INFO_STRUCT {
+ [case(0)] LPRPL_WKSTA_INFO_0 WkstaInfo0;
+ [case(1)] LPRPL_WKSTA_INFO_1 WkstaInfo1;
+ [case(2)] LPRPL_WKSTA_INFO_2 WkstaInfo2;
+} RPL_WKSTA_INFO_STRUCT, *PRPL_WKSTA_INFO_STRUCT, *LPRPL_WKSTA_INFO_STRUCT;
+
+//
+// Function Prototypes
+//
+
+//
+// Service apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplOpen(
+ [in,string,unique] RPL_NAME ServerName,
+ [out] LPRPL_RPC_HANDLE ServerHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplClose(
+ [in,out] LPRPL_RPC_HANDLE ServerHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_INFO_STRUCT InfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_INFO_STRUCT InfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// ADAPTER apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_ADAPTER_INFO_STRUCT AdapterInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_ADAPTER_ENUM AdapterEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+
+//
+// BOOT apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_BOOT_INFO_STRUCT BootInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR BootName,
+ [in,string] LPTSTR VendorName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_BOOT_ENUM BootEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// CONFIG apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_CONFIG_INFO_STRUCT ConfigInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ConfigName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName,
+ [in,out] LPRPL_CONFIG_ENUM ConfigEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// PROFILE apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileClone(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR SourceProfileName,
+ [in,string] LPTSTR TargetProfileName,
+ [in,string,unique] LPTSTR TargetProfileComment
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR AdapterName,
+ [in,out] LPRPL_PROFILE_ENUM ProfileEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR ProfileName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// VENDOR apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_VENDOR_INFO_STRUCT VendorInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR VendorName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,out] LPRPL_VENDOR_ENUM VendorEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+
+//
+// WKSTA apis
+//
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaAdd(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfo,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaClone(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR SourceWkstaName,
+ [in,string] LPTSTR TargetWkstaName,
+ [in,string,unique] LPTSTR TargetWkstaComment,
+ [in,string] LPTSTR TargetAdapterName,
+ [in] DWORD TargetWkstaIpAddress
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaDel(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaEnum(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR ProfileName,
+ [in,out] LPRPL_WKSTA_ENUM WkstaEnum,
+ [in] DWORD PrefMaxLength,
+ [out] LPDWORD TotalEntries,
+ [in,out,unique] LPDWORD ResumeHandle
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaGetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct
+ );
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaSetInfo(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string] LPTSTR WkstaName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ [in,out,unique] LPDWORD ErrorParameter
+ );
+
+//
+// SECURITY api
+//
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetSecurity(
+ [in] RPL_RPC_HANDLE ServerHandle,
+ [in,string,unique] LPTSTR WkstaName,
+ [in] DWORD WkstaRid,
+ [in] DWORD RplUserRid
+ );
+
+
+}
+
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/rplsvc_c.acf b/private/net/svcdlls/rpl/rplsvc_c.acf
new file mode 100644
index 000000000..a1e493fb0
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc_c.acf
@@ -0,0 +1,33 @@
+[implicit_handle (handle_t rplsvc_handle)]
+
+interface rplsvc
+
+{
+
+typedef [allocate(all_nodes)] LPRPL_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_BOOT_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_CONFIG_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_PROFILE_INFO_2;
+
+typedef [allocate(all_nodes)] LPRPL_VENDOR_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_VENDOR_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_ADAPTER_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_ADAPTER_INFO_1;
+
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_0;
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_1;
+typedef [allocate(all_nodes)] LPRPL_WKSTA_INFO_2;
+
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/rplsvc_s.acf b/private/net/svcdlls/rpl/rplsvc_s.acf
new file mode 100644
index 000000000..157ff2912
--- /dev/null
+++ b/private/net/svcdlls/rpl/rplsvc_s.acf
@@ -0,0 +1,7 @@
+[implicit_handle (handle_t rplsvc_handle)]
+
+interface rplsvc
+
+{
+}
+ \ No newline at end of file
diff --git a/private/net/svcdlls/rpl/server/adapter.c b/private/net/svcdlls/rpl/server/adapter.c
new file mode 100644
index 000000000..95756c78c
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/adapter.c
@@ -0,0 +1,502 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ adapter.c
+
+Abstract:
+
+ Adapter APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#define RPLADAPTER_ALLOCATE
+#include "adapter.h"
+#undef RPLADAPTER_ALLOCATE
+#include "database.h"
+
+
+
+DWORD AdapterGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case ADAPTER_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ FieldIndex].ColumnId, LocalBuffer,
+ sizeof( LocalBuffer), &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplAdapterInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplAdapterInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD AdapterGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ Error = AdapterGetField( pSession, ADAPTER_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = AdapterGetField( pSession, ADAPTER_AdapterComment, &Info->AdapterComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( AdapterName == NULL) {
+ Error = AdapterGetField( pSession, ADAPTER_AdapterName, &Info->AdapterName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( AdapterName) + 1) * sizeof(WCHAR);
+ Info->AdapterName = MIDL_user_allocate( DataSize);
+ if ( Info->AdapterName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, ( "AdapterName=0x%x", Info->AdapterName));
+ memcpy( Info->AdapterName, AdapterName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID AdapterGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ switch( Level) {
+ case 0: {
+ LPRPL_ADAPTER_INFO_0 Info = Buffer;
+ if ( Info->AdapterName != NULL) {
+ MIDL_user_free( Info->AdapterName);
+ }
+ if ( Info->AdapterComment != NULL) {
+ MIDL_user_free( Info->AdapterComment);
+ }
+ break;
+ }
+ }
+}
+
+
+DWORD AdapterSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info = Buffer;
+ switch( Level) {
+ case 1:
+ //
+ // Must initialize Flags - or will trap in GetField.
+ //
+ {
+ *pErrorParameter = ADAPTER_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->AdapterComment != NULL) {
+ *pErrorParameter = ADAPTER_AdapterComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterComment].ColumnId,
+ Info->AdapterComment,
+ ( wcslen( Info->AdapterComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->AdapterName != NULL) {
+ *pErrorParameter = ADAPTER_AdapterName;
+ CallM( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterName].ColumnId,
+ Info->AdapterName,
+ ( wcslen( Info->AdapterName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_ADAPTER_INFO_STRUCT AdapterInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_ADAPTER_INFO_1 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+
+ Buffer = Info = AdapterInfoStruct->AdapterInfo1;
+ switch( Level) {
+ case 1:
+ if ( Info->Flags != 0) {
+ ErrorParameter = ADAPTER_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->AdapterComment)) {
+ ErrorParameter = ADAPTER_AdapterComment;
+ break;
+ }
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ ErrorParameter = ADAPTER_AdapterName;
+ break;
+ }
+ _wcsupr( Info->AdapterName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that AdapterName is available in the database.
+ //
+ if ( RplFind( pSession, ADAPTER_TABLE_TAG, Info->AdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->AdapterTableId, JET_prepInsert));
+
+ Error = AdapterSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->AdapterTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR AdapterName
+ )
+/*++
+ If AdapterName is provided then delete matching record, else delete all
+ adapter records.
+--*/
+{
+ JET_ERR JetError;
+ DWORD Error = NO_ERROR;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, FALSE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ if ( AdapterName != NULL) {
+ _wcsupr( AdapterName);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( AdapterName != NULL) {
+ if ( !RplFind( pSession, ADAPTER_TABLE_TAG, AdapterName)) {
+ Error = NERR_RplAdapterNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->AdapterTableId));
+ } else {
+ CallJ( JetSetCurrentIndex( pSession->SesId, pSession->AdapterTableId, ADAPTER_INDEX_AdapterName));
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+ do {
+ CallJ( JetDelete( pSession->SesId, pSession->AdapterTableId));
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveNext, 0);
+ } while ( JetError == JET_errSuccess);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplAdapterEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN OUT LPRPL_ADAPTER_ENUM AdapterEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrAdapterEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ //
+ // For now we here we disallow all levels but 0 level. All other code
+ // would work for levels 0 and 1.
+ //
+ switch( AdapterEnum->Level) {
+ case 1:
+ TypicalSize = CoreSize = sizeof( RPL_ADAPTER_INFO_1);
+ NOTHING; // fall through
+ case 0:
+ if ( AdapterEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_ADAPTER_INFO_0);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of AdapterName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of AdapterComment
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, (
+ "AdapterEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ AdapterEnum->AdapterInfo.Level0->Buffer = (LPRPL_ADAPTER_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, ADAPTER_TABLE_TAG, NULL, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = AdapterGetInfo( pSession, NULL, AdapterEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->AdapterTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ AdapterGetInfoCleanup( AdapterEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ AdapterGetInfoCleanup( AdapterEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_ADAPTER, ("AdapterEnum: EntriesRead = 0x%x", EntriesRead));
+
+ AdapterEnum->AdapterInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ AdapterEnum->AdapterInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, NULL,
+ ((LPRPL_ADAPTER_INFO_0)(Buffer-CoreSize))->AdapterName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/apisec.c b/private/net/svcdlls/rpl/server/apisec.c
new file mode 100644
index 000000000..7fbd8184d
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/apisec.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ rplsec.c
+
+Abstract:
+
+ This module contains the remote boot service support routines
+ that create security objects and enforce security _access checking.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 06 - November - 1992
+
+Revision History:
+
+ 06-Nov-1992 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "apisec.h"
+#include <netlibnt.h> // NetpNtStatusToApiStatus
+#include <secobj.h> // ACE_DATA
+
+//
+// Structure that describes the mapping of Generic access rights to
+// object specific access rights for the remote boot service security object.
+//
+GENERIC_MAPPING RG_SecurityMapping = {
+ STANDARD_RIGHTS_READ | // Generic read
+ RPL_RECORD_ENUM |
+ RPL_RECORD_GET_INFO,
+ STANDARD_RIGHTS_WRITE | // Generic write
+ RPL_RECORD_ADD |
+ RPL_RECORD_SET_INFO |
+ RPL_RECORD_DEL,
+ STANDARD_RIGHTS_EXECUTE, // Generic execute
+ RPL_RECORD_ALL_ACCESS | // Generic all
+ RPL_RECORD_CLONE
+ };
+
+PSECURITY_DESCRIPTOR RG_SecurityDescriptor;
+
+
+NET_API_STATUS RplCreateSecurityObject( VOID)
+/*++
+
+Routine Description:
+
+ This function creates the remote bootr user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ NTSTATUS status;
+
+ //
+ // Order matters! These ACEs are inserted into the DACL in the
+ // following order. Security access is granted or denied based on
+ // the order of the ACEs in the DACL.
+ //
+ // LocalGroupAdmins are fow now allowed to perform all remote boot
+ // Service operations. Everybody else is denied.
+ //
+
+ ACE_DATA aceData[] = {
+ {ACCESS_ALLOWED_ACE_TYPE, 0, 0, GENERIC_ALL, &AliasAdminsSid}
+ };
+
+
+ status = NetpCreateSecurityObject(
+ aceData, // pAceData
+ sizeof(aceData) / sizeof(aceData[0]), // countAceData
+ NULL, // OwnerSid
+ NULL, // PrimaryGroupSid
+ &RG_SecurityMapping, // GenericToSpecificMapping
+ &RG_SecurityDescriptor // ppNewDescriptor
+ );
+
+ if ( ! NT_SUCCESS (status)) {
+ RplDump( ++RG_Assert, ( "status = 0x%x", status));
+ return NetpNtStatusToApiStatus( status);
+ }
+
+ return( NO_ERROR);
+}
+
+
+
+DWORD RplDeleteSecurityObject( VOID)
+/*++
+
+Routine Description:
+
+ This function destroys the remote boot service user-mode configuration
+ information object which is represented by a security descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS code
+
+--*/
+{
+ return( NetpDeleteSecurityObject( &RG_SecurityDescriptor));
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/apisec.h b/private/net/svcdlls/rpl/server/apisec.h
new file mode 100644
index 000000000..4393620e3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/apisec.h
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1987-94 Microsoft Corporation
+
+Module Name:
+
+ rplsec.h
+
+Abstract:
+
+ Functions exported by rplsec.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - April - 1994
+
+Revision History:
+
+ 05-Apr-1994 vladimv
+ Created
+
+--*/
+
+NET_API_STATUS RplCheckSecurity( ACCESS_MASK DesiredAccess);
+NET_API_STATUS RplCreateSecurityObject( VOID);
+NET_API_STATUS RplDeleteSecurityObject( VOID);
+
+//
+// Object specific access masks
+//
+
+#define RPL_RECORD_ADD 0x0001
+#define RPL_RECORD_CLONE 0x0002
+#define RPL_RECORD_DEL 0x0004
+#define RPL_RECORD_ENUM 0x0008
+#define RPL_RECORD_GET_INFO 0x0010
+#define RPL_RECORD_SET_INFO 0x0020
+
+#define RPL_RECORD_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | \
+ RPL_RECORD_ADD | \
+ RPL_RECORD_DEL | \
+ RPL_RECORD_ENUM | \
+ RPL_RECORD_CLONE | \
+ RPL_RECORD_SET_INFO | \
+ RPL_RECORD_GET_INFO )
+
+
+#define SECURITY_OBJECT L"RplSecurityObject"
+
+extern PSECURITY_DESCRIPTOR RG_SecurityDescriptor;
+extern GENERIC_MAPPING RG_SecurityMapping;
+
diff --git a/private/net/svcdlls/rpl/server/bbcfile.c b/private/net/svcdlls/rpl/server/bbcfile.c
new file mode 100644
index 000000000..6344ceb93
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/bbcfile.c
@@ -0,0 +1,1059 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ bbcfile.c
+
+Abstract:
+
+ Processes boot block configuration file.
+
+ Provides bbcfile functionality similar to that contained in init.c
+ & patch.c of LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+
+#include "local.h"
+#include "bbcfile.h"
+
+//
+// Checksum buffer size is chosen to be a multiple of 8 * sizeof( DWORD).
+// to speed up checksum algorithm below. This is not a requirement though.
+//
+#define CHK_SUM_BUF_SIZE ( 8 * sizeof( DWORD) * 256) // 8K
+
+#define NO_PATCH_OFFSET ((DWORD)-1)
+
+#define PARAM_INDEX 2 // index of parameters in sys/com/exe line
+//
+// PARAM_INDEX string when expressed in DBCS should not exceed 0xFF bytes.
+//
+#define MAX_SIZE_DBCS_PARAMS (0xFF+1)
+
+#define MEM_INDEX 3 // index of extra memory in sys/com/exe line
+#define MOVEABLE_INDEX 4 // set if driver can be moved after init
+
+//
+// MOVABLE_INDEX string may be take one of the following two values.
+//
+#define MOVEABLE_SWITCH L'M'
+#define EXEC_IN_LOW_MEM_SWITCH L'L' // undocumented switch value
+
+#define MAX_FLIST_LEN 255
+
+//
+// Indices used for parsing of lines in boot block configuration files.
+//
+
+#define CONF_LINE_ID 0 // index of line type
+#define CONF_LINE_FILE 1 // index of file (rel path) in config line
+#define CONF_LINE_BASE_ADDRESS 1 // index of file (rel path) in config line
+#define FIRST_FILE_NAME 3 // index of the first file name in LDR line
+
+#define MAX_CONFIG_FIELDS 8 // maximum number of fields in config line
+
+#define MIN_BBLOCK_BASE_SEG 0xc0
+#define TILDE_STRING L"~"
+#define SPACE_STRING L" "
+
+
+
+DWORD StringToDword( IN PWCHAR String)
+/*++
+ We would like to use generic base (0) but it does not work for
+ strings like "D0H". That is the reason why we first check if
+ the last character is 'H' or 'h'.
+--*/
+{
+ DWORD Length;
+
+ Length = wcslen( String);
+ if ( Length == 0) {
+ return( 0);
+ }
+ if ( String[ Length-1] == L'H' || String[ Length-1] == L'h') {
+ return( wcstoul( String, NULL, 16));
+ } else {
+ return( wcstoul( String, NULL, 0));
+ }
+}
+
+
+BOOL RplInitExeOrSys(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN OUT PFLIST_TBL pFlist
+ )
+/*++
+
+Routine Description:
+ Retreives the file type of a binary (exe/com/sys) file.
+
+Arguments:
+ pFlist - pointer to FLIST_TBL element
+
+Return Value:
+ TRUE if successful, else FALSE.
+
+--*/
+{
+ BYTE id_tbl[2];
+ HANDLE FileHandle;
+ DWORD bytes_read;
+ DWORD DbcsSize;
+ LPSTR DbcsString;
+ PWCHAR * WordTable;
+
+ FileHandle = RplFileOpen( pFlist->path_name);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ( "path_name=%ws", pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+
+ if ( !ReadFile( FileHandle, id_tbl, 2, &bytes_read, NULL)) {
+ RplDump( ++RG_Assert, ( "Error = %d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ (VOID)CloseHandle( FileHandle);
+ return( FALSE);
+ }
+ (VOID)CloseHandle( FileHandle);
+
+ if ( bytes_read == 2 && id_tbl[0] == 0x4d && id_tbl[1] == 0x5a) {
+ //
+ // Driver or executable file is in exe-format, set the bit in
+ // the type field. This info will be used on the client side.
+ //
+ pFlist->FileData.file_type |= IS_EXE_SYS;
+ }
+
+ WordTable = pWorkerData->WordTable;
+
+ if ( *WordTable[ PARAM_INDEX] == 0) {
+ pFlist->FileData.param_len = 0;
+ pFlist->param_list = (LPSTR)&RG_Null;
+ } else {
+ DbcsSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ WordTable[ PARAM_INDEX],
+ -1, // UNICODE string length not available
+ MAX_SIZE_DBCS_PARAMS,
+ &DbcsString
+ );
+ if ( DbcsSize == 0) {
+ RplDump( ++RG_Assert, ("WordTable=0x%x, string=%ws",
+ WordTable, WordTable[ PARAM_INDEX]));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ return( FALSE);
+ }
+ pFlist->FileData.param_len = (BYTE)(DbcsSize - 1);
+ pFlist->param_list = DbcsString;
+ }
+
+ pFlist->FileData.extra_mem = StringToDword( WordTable[ MEM_INDEX]);
+
+ if ( *WordTable[ MOVEABLE_INDEX] == MOVEABLE_SWITCH) {
+ pFlist->FileData.file_type |= IS_MOVEABLE;
+ } else if ( *WordTable[ MOVEABLE_INDEX] == EXEC_IN_LOW_MEM_SWITCH) {
+ pFlist->FileData.file_type |= EXEC_IN_LOW_MEM;
+ }
+ return( TRUE);
+}
+
+
+BOOL RplChecksum(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ IN HANDLE FileHandle,
+ OUT PWORD pChecksum
+ )
+/*++
+Routine Description:
+ If successful returns sum of all words in a file.
+
+Arguments:
+ FileHandle - handle of file to checksum
+ pChecksum - pointer to calculated checksum
+
+Return Value:
+ TRUE if success, FALSE otherwise
+--*/
+{
+ DWORD Checksum;
+ DWORD BytesRead;
+ PDWORD pDword;
+ DWORD Length;
+
+ if ( pWorkerData->ChecksumBuffer == NULL) {
+ pWorkerData->ChecksumBuffer = RplMemAlloc( pWorkerData->MemoryHandle,
+ CHK_SUM_BUF_SIZE);
+ if ( pWorkerData->ChecksumBuffer == NULL) {
+ return( FALSE);
+ }
+ }
+
+ Checksum = 0;
+
+ do {
+ //
+ // These are usually binary files so there are no DBCS/UNICODE
+ // issues. But even if we had a text file here, we should read
+ // it & checksum it raw (i.e. no conversion from DBCS to UNICODE)
+ // since it is the raw data that gets shipped to the client.
+ //
+ if ( !ReadFile( FileHandle,
+ pWorkerData->ChecksumBuffer,
+ CHK_SUM_BUF_SIZE,
+ &BytesRead,
+ NULL)) {
+ RplDump( ++RG_Assert, ( "Error = %d", GetLastError()));
+ return( FALSE);
+ }
+
+ //
+ // Round up to make it divisible by 4 == sizeof( *pDword).
+ // Note that extra bytes are set zero in the 'boot' block,
+ // the files are always on the boundary of paragraph.
+ //
+ if ( BytesRead & 1) { // make it divisible by 2
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ }
+ if ( BytesRead & 2) { // make it divisible by 4
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ pWorkerData->ChecksumBuffer[ BytesRead++] = 0;
+ }
+
+ //
+ // Checksum in chunks of 8, for speed.
+ //
+ for ( Length = BytesRead / 4,
+ pDword = (PDWORD)pWorkerData->ChecksumBuffer + Length;
+ Length > 8; Length -= 8) {
+ pDword -= 8;
+ Checksum += pDword[ 0];
+ Checksum += pDword[ 1];
+ Checksum += pDword[ 2];
+ Checksum += pDword[ 3];
+ Checksum += pDword[ 4];
+ Checksum += pDword[ 5];
+ Checksum += pDword[ 6];
+ Checksum += pDword[ 7];
+ }
+ // Then finish the checksum.
+ //
+ for ( ; Length > 0; Length--) {
+ Checksum += *(--pDword);
+ }
+ } while (BytesRead == CHK_SUM_BUF_SIZE); // exit read loop when all read
+
+ *pChecksum = LOWORD( Checksum) + HIWORD( Checksum);
+ return( TRUE);
+}
+
+
+BOOL RplInitFileList(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR rel_path,
+ IN OUT PFLIST_TBL pFlist,
+ IN OUT PDWORD cur_para_ptr,
+ IN DWORD file_type
+ )
+/*++
+
+Routine Description:
+
+ Opens a file and initalizes the parameter item of the file list table.
+ It reads the file only if checksum is required and file is not of
+ RPL_BOOT_TYPE.
+
+Arguments:
+
+ rel_path - relative path name of file
+ pFlist - pointer to current element in file list table
+ cur_para_ptr - current paragraph
+ file_type - file type
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ HANDLE FileHandle;
+ LPSTR DbcsFileName;
+ DWORD DbcsFileNameSize;
+ DWORD Size;
+ BOOL Success;
+
+ Success = FALSE;
+ FileHandle = INVALID_HANDLE_VALUE;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++InitFileList(0x%x)", pWorkerData));
+
+ //
+ // get the full path name and the actual file name
+ //
+
+ Size = ( RG_DirectoryLength + wcslen( rel_path) + 1) * sizeof(WCHAR);
+ pFlist->path_name = RplMemAlloc( pWorkerData->MemoryHandle, Size);
+ if ( pFlist->path_name == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ goto cleanup;
+ }
+ wcscpy( pFlist->path_name, RG_Directory);
+ wcscat( pFlist->path_name, rel_path);
+
+ //
+ // Open the BBC file and get its length.
+ //
+
+ FileHandle = RplFileOpen( pFlist->path_name);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("path_name=%ws", pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ goto cleanup;
+ }
+ Size = GetFileSize( FileHandle, NULL);
+ if ( Size == INVALID_FILE_SIZE) {
+ RplDump( ++RG_Assert, ( "Error=%d, path_name=%ws",
+ GetLastError(), pFlist->path_name));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+ pFlist->FileData.file_len = Size;
+
+ //
+ // Calculate checksum for the file, if cheksums are done. Note that
+ // RPLBOOT.SYS is never checksummed because it modifies its own code
+ // before the checking the checksum.
+ //
+
+ if ( RG_ReadChecksum && file_type != RPL_BOOT_TYPE) {
+ if ( !RplChecksum( pWorkerData, FileHandle, &pFlist->FileData.chk_sum)) {
+ pFlist->FileData.chk_sum = NO_CHKSUM_USED; // for RPLBOOT to ingore checksum
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileChecksum;
+ }
+ } else {
+ pFlist->FileData.chk_sum = NO_CHKSUM_USED; // RPLBOOT is never checksummed
+ }
+
+ //
+ // Note that FileName is UNICODE, while client needs to receive a DBCS
+ // version of this name
+ //
+ pFlist->FileName = RplGetLastPathComponent( pFlist->path_name);
+
+ DbcsFileNameSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ pFlist->FileName, // UNICODE string to convert
+ -1, // UNICODE string length not available
+ MAX_SIZEOF_DBCS_PATH,
+ &DbcsFileName
+ );
+ if ( DbcsFileNameSize == 0) {
+ RplDump( ++RG_Assert, ("FileName=%ws", pFlist->FileName));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pFlist->path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+
+ pFlist->DbcsFileName = DbcsFileName;
+ pFlist->DbcsFileNameSize = DbcsFileNameSize;
+
+ //
+ // Length of file in paragraphs, rounded up to the next paragraph.
+ // The maximum length could be checked, but let it be.
+ // Set the relative position of file from the start of file block
+ //
+ pFlist->FileData.file_len = (pFlist->FileData.file_len + 15) & 0xfffffff0L;
+
+ pFlist->FileData.file_type = (WORD)file_type;
+ pFlist->FileData.param_len = 0;
+ pFlist->FileData.extra_mem = 0;
+ pFlist->FileData.param_offset = 0;
+ pFlist->FileData.name_offset = 0;
+ pFlist->param_list = NULL;
+
+ //
+ // There may be several instances of the same file in the boot block.
+ // For example, Nokia NetStation memory extender LOADHI.SYS can load
+ // device drivers to memory between C000 - FFFF. PROTMAN.SYS, NDIS and
+ // NETBEUI may all be loaded to there. These boot block lines
+ // load the whole DOS NDIS stack to the memory above C000
+ // DAT NETBEUI.DOS
+ // DRV LOADHI.SYS NETBEUI.DOS ~
+ // DAT IBMTOK.DOS
+ // DRV LOADHI.SYS IBMTOK.DOS ~
+ // DAT PROTMAN.DOS
+ // DRV LOADHI.SYS PROTMAN.DOS~I:\ ~
+ //
+ // The code to send LOADHI.SYS once (instead of three times) in the boot
+ // block, is not worth of effort, since LOADHI.SYS is small (~4kB).
+ //
+
+ pFlist->FileData.file_addr = *cur_para_ptr * 16;
+ *cur_para_ptr += (pFlist->FileData.file_len / 16);
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--InitFileList(0x%x)", pWorkerData));
+ Success = TRUE;
+
+cleanup:
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ return( Success);
+}
+
+
+LPWSTR * RplBbcFileToTable( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ Reads boot block file to a buffer. Skips comment lines and copies
+ non-comment lines to the string buffer. Returns pointer to the array
+ of pointers to lines, and the length of table.
+
+Arguments:
+
+Return Value:
+ Pointer to line table if successful, NULL otherwise.
+
+--*/
+{
+#define RPL_LINE_END L"\n\r"
+ DWORD index;
+ LPWSTR * Table;
+ PWCHAR UnicodeString;
+ PWCHAR pWchar;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcFileToTable(0x%x)", pWorkerData));
+
+ UnicodeString = RplReadTextFile( pWorkerData->MemoryHandle,
+ pWorkerData->BbcFile, MAX_BBC_FILE_SIZE);
+ if ( UnicodeString == NULL) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws", pWorkerData->BbcFile));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( NULL);
+ }
+
+ Table = RplMemAlloc( pWorkerData->MemoryHandle, (MAX_FLIST_LEN+1)* sizeof( LPWSTR));
+ if ( Table == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( NULL);
+ }
+
+ for ( pWchar = wcstok( UnicodeString, RPL_LINE_END), index = 0;
+ pWchar != NULL && index < MAX_FLIST_LEN;
+ pWchar = wcstok( NULL, RPL_LINE_END)) {
+
+ while( iswspace( *pWchar)) { // skip empty chars at the beginning
+ pWchar++;
+ }
+ if ( *pWchar == 0 || *pWchar == L';') {
+ continue; // don't save blank or comment lines
+ }
+
+ Table[ index++] = pWchar;
+ }
+
+ if ( pWchar != NULL) {
+ //
+ // Error case. Too many lines in the file.
+ //
+ RplDump( ++RG_Assert, ( "pWchar=0x%x", pWchar));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileLineCount;
+ Table = NULL;
+ } else {
+ Table[ index] = NULL; // terminate the table
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcFileToTable(0x%x)", pWorkerData));
+ return( Table);
+
+} // RplBbcFileToTable()
+
+
+
+BOOL RplBbcLineToTable(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR Line
+ )
+/*++
+
+Routine Description:
+
+ Copies a line into a buffer. Breaks the copy into "words" (substrings).
+ Makes an array of pointers to "words". The original line string is kept
+ unchanged because it is needed for error reporting in the caller.
+
+Arguments:
+ Line - ptr to line string
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+ FALSE is returned if we ran out of memory, or if line has too many words.
+
+--*/
+{
+ DWORD index;
+ DWORD BufferSize;
+ LPWSTR Cursor;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcLineToTable(0x%x):%ws", pWorkerData, Line));
+
+ BufferSize = (wcslen( Line) + 1) * sizeof( WCHAR);
+
+ if ( pWorkerData->LineBuffer == NULL) {
+
+ pWorkerData->LineBuffer = RplMemAlloc( pWorkerData->MemoryHandle, BufferSize);
+ if ( pWorkerData->LineBuffer == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ pWorkerData->LineBufferSize = BufferSize;
+
+ } else if ( BufferSize > pWorkerData->LineBufferSize) {
+
+ RplMemFree( pWorkerData->MemoryHandle, pWorkerData->LineBuffer);
+ pWorkerData->LineBuffer = RplMemAlloc( pWorkerData->MemoryHandle, BufferSize);
+ if ( pWorkerData->LineBuffer == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ pWorkerData->LineBufferSize = BufferSize;
+ }
+
+ if ( pWorkerData->WordTable == NULL) {
+ pWorkerData->WordTable = RplMemAlloc(
+ pWorkerData->MemoryHandle,
+ (MAX_CONFIG_FIELDS + 1) * sizeof(LPWSTR)
+ );
+ if ( pWorkerData->WordTable == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ }
+
+ memcpy( pWorkerData->LineBuffer, Line, BufferSize);
+
+ for ( index = 0, Cursor = wcstok( pWorkerData->LineBuffer, SPACE_STRING);
+ index < MAX_CONFIG_FIELDS && Cursor != NULL;
+ index++, Cursor = wcstok( NULL, SPACE_STRING)) {
+
+ if ( wcscmp( Cursor, TILDE_STRING) == 0) {
+ //
+ // Single tilda is just an empty placeholder.
+ //
+ pWorkerData->WordTable[ index] = (LPWSTR)&RG_Null;
+
+ } else {
+ pWorkerData->WordTable[ index] = Cursor;
+ //
+ // In unlikely case there are tildas embedded with a filename
+ // (e.g. driver with its parameters), replace them tildas with spaces.
+ //
+ while ( (Cursor = wcsrchr( Cursor, TILDE_CHAR)) != NULL) {
+ *Cursor++ = SPACE_CHAR;
+ }
+ }
+ }
+
+ if ( Cursor != NULL) {
+ //
+ // Boot block config line has too many items.
+ //
+ RplDump( ++RG_Assert, ( "Cursor=0x%x", Cursor));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = Line;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+
+ while ( index < MAX_CONFIG_FIELDS) {
+ pWorkerData->WordTable[ index++] = (LPWSTR)&RG_Null; // fill in the rest
+ }
+ pWorkerData->WordTable[ index] = NULL; // then null terminate
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcLineToTable(0x%x):%ws", pWorkerData, Line));
+ return( TRUE);
+}
+
+
+CONFIG_TYPE ConfigTypeTable[] = {
+ { L"RPL", RPL_BOOT_TYPE},
+ { L"ORG", ORG_TYPE},
+ { L"DAT", DATA_FILE},
+ { L"LDR", BINARY_LOADER},
+ { L"DRV", DRV_TYPE},
+ { L"EXE", EXE_TYPE},
+ { L"BASE", BASE_TYPE},
+ { NULL, UNKNOWN_CONFIG_TYPE}
+};
+
+DWORD RplConfigLineType( IN LPWSTR ConfigLineId)
+/*++
+
+Routine Description:
+
+ Returns the type of the current line in boot block config file.
+
+Arguments:
+
+ Pointer to identifier for the current line.
+
+Return Value:
+
+ Config line type.
+
+--*/
+{
+ DWORD Type;
+ PCONFIG_TYPE pConfigType;
+
+ for ( Type = UNKNOWN_CONFIG_TYPE, pConfigType = ConfigTypeTable;
+ pConfigType->id != 0;
+ pConfigType++) {
+ if ( !wcscmp( ConfigLineId, pConfigType->id )) {
+ Type = pConfigType->type;
+ break;
+ }
+ }
+ return( Type);
+}
+
+
+BOOL RplCheckBootHeader(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN LPWSTR FilePath,
+ OUT PDWORD pFirstOffset
+ )
+/*++
+
+Routine Description:
+ Reads patch offsets from the start of DLCLOADR.COM file.
+
+Arguments:
+ FilePath - path name of DLCLOADR.COM
+ pFirstOffset - ptr to first NLS patch offset
+
+Return Value:
+ TRUE if successful, FALSE otherwise.
+
+--*/
+{
+ HANDLE FileHandle;
+ DWORD read_len;
+ WORD offset_buf[ OFFSET_BUF_LEN];
+ PRPLBOOT_HEADER pRplbootHdr;
+ BOOL Success;
+
+ Success = FALSE;
+
+ FileHandle = RplFileOpen( FilePath);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ goto cleanup;
+ }
+
+ if ( !ReadFile( FileHandle, (PBYTE)offset_buf,
+ OFFSET_BUF_LEN * sizeof(WORD), &read_len, NULL)) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ goto cleanup;
+ }
+
+ pRplbootHdr = (PRPLBOOT_HEADER)((PBYTE)offset_buf + OFFSET_RPLBOOT_HDR);
+
+ if ( !strcmp( pRplbootHdr->achIdStamp, "RPL" ) && // BUGBUG hardcoded constants
+ pRplbootHdr->bBbVersion == BBVERSION_10 ) {
+ //
+ // check that rplboot.sys nls patch version match or
+ // it does not require NLS patching at all
+ //
+ if (pRplbootHdr->bNlsVersion == NLS_VERSION_10) {
+ //
+ // get the offset of NLS patches in RPLBOOT.SYS
+ //
+ *pFirstOffset = pRplbootHdr->usNlsPatchOff;
+ } else if (pRplbootHdr->bNlsVersion == 0) {
+ //
+ // No NLS patching, if version number is 0, that's OK.
+ //
+ *pFirstOffset = NO_PATCH_OFFSET;
+ } else {
+ //
+ // Configuration error: RPLBOOT.SYS expects to get a
+ // different NLS patching from one that RPLSERVR can provide,
+ // the versions are incompatible.
+ //
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaWrongVersion;
+ goto cleanup;
+ }
+ } else {
+ //
+ // Configuration error: the header is missing, this is an old
+ // (or wrong) RPLBOOT.sys.
+ //
+ RplDump( ++RG_Assert, ( "FilePath=%ws", FilePath));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = FilePath;
+ pWorkerData->EventId = NELOG_RplWkstaWrongVersion;
+ goto cleanup;
+ }
+ Success = TRUE;
+
+cleanup:
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ return( Success);
+}
+
+
+BOOL RplBbcFile( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+
+ Processes the boot block configuration file. Initializes file list table.
+
+Arguments:
+
+Return Value:
+ TRUE if success, FALSE otherwise
+
+--*/
+{
+ LPWSTR * LineTable; // ptr to array of line-strings for BBC file
+ DWORD LoaderLineIndex; // index of a loader line in BBC file
+ LPWSTR * LoaderWordTable; // ptr to array of word-strings for loader line
+ LPWSTR LoaderBuffer; // buffer for LoaderWordTable
+ PRESOURCE_TBL resource_tbl;
+ DWORD resource_tbl_size;
+ DWORD resource_tbl_len;
+ LPWSTR String;
+ DWORD Index;
+ DWORD org_addr;
+ DWORD cur_para; // current length of boot block image in paragraphs
+ DWORD fblock_base;
+ DWORD bblock_base; // no default base address
+ PFLIST_TBL flist_tbl; // file list array
+ DWORD flist_tbl_len; // # of elements in flist_tbl[]
+ DWORD flist_tbl_size; // size in bytes of flist_tbl[]
+ DWORD FileIndex; // index entries in flist_tbl[]
+ BOOL org_is_set;
+ DWORD line_type;
+ DWORD PatchOffset;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++BbcFile(0x%x)", pWorkerData));
+
+ pWorkerData->loader_i = pWorkerData->rplboot_i = MAXWORD;
+ fblock_base = bblock_base = 0;
+ resource_tbl_size = resource_tbl_len = 0;
+ org_is_set = FALSE;
+ cur_para = 0;
+
+ //
+ // Read boot block configuration file to a UNICODE string table, where
+ // each string from this table contains a single line from BBC file.
+ // This table is null terminated.
+ //
+ LineTable = RplBbcFileToTable( pWorkerData);
+ if ( LineTable == NULL) {
+ return( FALSE);
+ }
+
+ flist_tbl = RplMemAlloc( pWorkerData->MemoryHandle, sizeof(FLIST_TBL) * MAX_FLIST_LEN);
+ if ( flist_tbl == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+
+ //
+ // Process lines in the boot block configuration file and
+ // insert data to file list
+ //
+ for ( Index = FileIndex = 0; (String = LineTable[ Index]) != NULL; Index++) {
+ //
+ // Break up single line from boot block config file into word table.
+ //
+ if ( !RplBbcLineToTable( pWorkerData, String)) {
+ return( FALSE);
+ }
+
+ line_type = RplConfigLineType( pWorkerData->WordTable[ CONF_LINE_ID]);
+
+ switch ( line_type) {
+
+ case RPL_BOOT_TYPE: // There must be a single entry like this.
+ if ( pWorkerData->rplboot_i != MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws", pWorkerData->BbcFile, String));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ pWorkerData->rplboot_i = FileIndex; // fall through !!
+
+ case BINARY_LOADER: // There must be a single entry like this.
+ if (line_type == BINARY_LOADER) {
+ if ( pWorkerData->loader_i != MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws, loader_i=0x%x",
+ pWorkerData->BbcFile, String, pWorkerData->loader_i));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ pWorkerData->loader_i = FileIndex;
+ } // fall through !!
+
+ case EXE_TYPE:
+ case DRV_TYPE:
+ case DATA_FILE:
+ if ( !RplInitFileList( pWorkerData,
+ pWorkerData->WordTable[ CONF_LINE_FILE],
+ &flist_tbl[ FileIndex], &cur_para, line_type)) {
+ return( FALSE);
+ }
+ if ( line_type == DRV_TYPE || line_type == EXE_TYPE) {
+ if ( !RplInitExeOrSys( pWorkerData, &flist_tbl[ FileIndex])) {
+ return( FALSE );
+ }
+ }
+
+ //
+ // Update the minimum size of header.
+ // Reserve space for 0xa 0xd in the end of param list
+ //
+ if ( flist_tbl[ FileIndex].FileData.param_len) {
+ pWorkerData->min_wksta_buf += (flist_tbl[ FileIndex].FileData.param_len + 2);
+ }
+ pWorkerData->min_wksta_buf += flist_tbl[ FileIndex].DbcsFileNameSize;
+ FileIndex++;
+ break;
+
+ case ORG_TYPE:
+ org_addr = StringToDword( pWorkerData->WordTable[ CONF_LINE_FILE]);
+ //
+ // file block base must be above 0C0 (paras)
+ //
+ fblock_base = org_addr - cur_para - bblock_base;
+ org_is_set = TRUE;
+ if ( cur_para + bblock_base > org_addr) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, cur_para=0x%x,"
+ " bblock_base=0x%x, org_addr=0x%x",
+ pWorkerData->BbcFile, cur_para, bblock_base, org_addr));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+ break;
+
+ case BASE_TYPE:
+ bblock_base = StringToDword( pWorkerData->WordTable[ CONF_LINE_BASE_ADDRESS]);
+ if ( org_is_set || bblock_base < MIN_BBLOCK_BASE_SEG) {
+ //
+ // Origin has been set to a wrong address or
+ // boot block base address is too low.
+ //
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, org_is_set=%d, bblock_base=0x%x",
+ pWorkerData->BbcFile, org_is_set, bblock_base));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+ break;
+
+ default:
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, String=%ws", pWorkerData->BbcFile, String));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = String;
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ } // switch( line_type)
+
+ if ( line_type == BINARY_LOADER) {
+ //
+ // We have to save loader line data for processing below.
+ //
+ LoaderWordTable = pWorkerData->WordTable;
+ LoaderBuffer = pWorkerData->LineBuffer;
+ pWorkerData->LineBuffer = NULL;
+ pWorkerData->WordTable = NULL;
+ LoaderLineIndex = Index;
+ }
+ }
+
+ //
+ // Boot block configuration file must contain a boot line (RPLBOOT.SYS)
+ // and a loader line (RPLSTART.COM or OS2LDR).
+ //
+
+ if ( pWorkerData->rplboot_i == MAXWORD || pWorkerData->loader_i == MAXWORD) {
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, rplboot_i=0x%x, loader_i=0x%x\n",
+ pWorkerData->BbcFile, pWorkerData->rplboot_i, pWorkerData->loader_i));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_RplWkstaBbcFile;
+ return( FALSE);
+ }
+
+ //
+ // Do not bother to resize file list table - it will be freed shortly
+ // anyway, just save the relevant size of table for later use.
+ //
+ flist_tbl_len = FileIndex;
+ flist_tbl_size = flist_tbl_len * sizeof(FLIST_TBL);
+
+ //
+ // Check the loader parameters. This code does real work for OS/2 only.
+ // For DOS it just fills the null table entry.
+ //
+
+ //
+ // Count number of items in the resource table.
+ //
+ for ( resource_tbl_len = 0;
+ *LoaderWordTable[ FIRST_FILE_NAME + resource_tbl_len]; resource_tbl_len++) {
+ NOTHING;
+ }
+ resource_tbl_len++; // leave space for terminating null
+ resource_tbl_size = resource_tbl_len * sizeof( RESOURCE) + sizeof( WORD);
+ resource_tbl = RplMemAlloc( pWorkerData->MemoryHandle, resource_tbl_size);
+ if ( resource_tbl == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ if ( resource_tbl_len > MAXWORD) {
+ RplDump( ++RG_Assert, ( "resource_tbl_len=%d", resource_tbl_len));
+ return( FALSE);
+ }
+ resource_tbl->entries = (WORD)resource_tbl_len;
+
+ //
+ // get the file names in the reource table of OS2LDR
+ //
+ for ( Index = 0; *(String = LoaderWordTable[ FIRST_FILE_NAME + Index]) != 0; Index++) {
+
+ String = RplGetLastPathComponent( String); // convert file path to file name
+
+ //
+ // Verify that resource file is a part of a boot block.
+ //
+ for ( FileIndex = 0;
+ FileIndex < flist_tbl_len
+ && _wcsicmp( String, flist_tbl[ FileIndex].FileName);
+ FileIndex++) {
+ NOTHING;
+ }
+ if ( FileIndex == flist_tbl_len) {
+ //
+ // resource file is not a part of a boot block
+ //
+ RplDump( ++RG_Assert, ( "BbcFile=%ws, Line=%ws",
+ pWorkerData->BbcFile, LineTable[ LoaderLineIndex]));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventStrings[ 2] = LineTable[ LoaderLineIndex];
+ pWorkerData->EventId = NELOG_Invalid_Config_Line;
+ return( FALSE);
+ }
+ //
+ // initialize the next item in resource table
+ //
+ resource_tbl->file_tbl[ Index].pos_in_paras = (WORD)( flist_tbl[ FileIndex].FileData.file_addr >> 4);
+ resource_tbl->file_tbl[ Index].file_len = flist_tbl[ FileIndex].FileData.file_len;
+ }
+ resource_tbl->file_tbl[ Index].pos_in_paras = 0;
+ resource_tbl->file_tbl[ Index].file_len = 0L;
+
+ //
+ // Clean up.
+ //
+ RplMemFree( pWorkerData->MemoryHandle, LoaderWordTable);
+ RplMemFree( pWorkerData->MemoryHandle, LoaderBuffer);
+
+ //
+ // Get the minimum size of workstation specific data
+ //
+ pWorkerData->min_wksta_buf += flist_tbl_size + resource_tbl_size;
+
+ pWorkerData->file_block_base = fblock_base << 4;
+ pWorkerData->boot_block_base = bblock_base << 4;
+ pWorkerData->flist_tbl = flist_tbl;
+ pWorkerData->flist_tbl_len = flist_tbl_len;
+ pWorkerData->resource_tbl = resource_tbl;
+ pWorkerData->resource_tbl_size = resource_tbl_size;
+
+ //
+ // Check header of RPLBOOT.SYS.
+ //
+ if( !RplCheckBootHeader( pWorkerData, flist_tbl->path_name, &PatchOffset)) {
+ return( FALSE);
+ }
+
+ //
+ // RPLBOOT.SYS has a good header, see if it requires patching.
+ //
+ if ( PatchOffset == NO_PATCH_OFFSET) {
+ pWorkerData->MakePatch = FALSE; // RPLBOOT.SYS requires no patching.
+ } else {
+ pWorkerData->MakePatch = TRUE;
+ pWorkerData->PatchOffset = flist_tbl[ pWorkerData->rplboot_i].FileData.file_addr + PatchOffset;
+ flist_tbl[ pWorkerData->rplboot_i].FileData.chk_sum = NO_CHKSUM_USED;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--BbcFile(0x%x)", pWorkerData));
+ return( TRUE);
+
+} // RplBbcFile()
+
+
+
diff --git a/private/net/svcdlls/rpl/server/bbcfile.h b/private/net/svcdlls/rpl/server/bbcfile.h
new file mode 100644
index 000000000..2c3486709
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/bbcfile.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ bbcfile.h
+
+Abstract:
+
+ Exports from bbcfile.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplBbcFile( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
+
diff --git a/private/net/svcdlls/rpl/server/boot.c b/private/net/svcdlls/rpl/server/boot.c
new file mode 100644
index 000000000..3c8bed16b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/boot.c
@@ -0,0 +1,832 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ boot.c
+
+Abstract:
+
+ This module contains RPL boot apis: - internal routines only.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - November - 1993
+
+Revision History:
+
+ 10-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "config.h"
+#include "profile.h"
+#include "wksta.h"
+#define RPLBOOT_ALLOCATE
+#include "boot.h"
+#undef RPLBOOT_ALLOCATE
+#include "database.h"
+
+
+
+DWORD BootGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case BOOT_WindowSize:
+ case BOOT_Flags:
+ case BOOT_VendorId:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplBootInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplBootInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ RPL_RETURN( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+BOOL BootResumeFirst(
+ IN PRPL_SESSION pSession,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first boot record following the boot record
+ described by the resume handle.
+--*/
+{
+ BYTE ResumeValue[ RPL_MAX_VENDOR_NAME_SIZE + RPL_MAX_WKSTA_NAME_SIZE];
+ PWCHAR BootName;
+ DWORD BootNameSize;
+ DWORD ResumeSize;
+ JET_ERR JetError;
+
+ *pTableEnd = FALSE;
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errRecordNotFound
+ || JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+ if ( !(ARGUMENT_PRESENT( pResumeHandle)) || *pResumeHandle == 0) {
+ return( TRUE); // not resuming, all done
+ }
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, ResumeValue, sizeof( DWORD), JET_bitNewKey));
+ BootName = (PWCHAR)( ResumeValue + sizeof(DWORD));
+ BootNameSize = (DWORD)( ResumeSize - sizeof(DWORD));
+ if ( BootNameSize != (wcslen( BootName) + 1) * sizeof( WCHAR)) {
+ RplDump( ++RG_Assert, ("ResumeValue=0x%x, ResumeSize=0x%x", ResumeValue, ResumeSize));
+ return( FALSE);
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, BootNameSize, 0));
+ CallB( JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekGT));
+ return( TRUE);
+}
+
+
+VOID BootResumeSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN DWORD VendorId,
+ IN PWCHAR BootName,
+ IN PDWORD pResumeHandle
+ )
+/*++
+ This all other other functions that save resume keys is void, because there
+ is no documented way of returning error cannot resume to the client.
+--*/
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)];
+ DWORD BootSize;
+
+ memcpy( ResumeBuffer, &VendorId, sizeof( VendorId));
+ BootSize = ( wcslen( BootName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + sizeof(VendorId), BootName, BootSize);
+ (VOID)ResumeKeySet( pSession, (DWORD)ServerHandle, ResumeBuffer, BootSize+sizeof(VendorId), pResumeHandle);
+}
+
+
+DWORD BootFilterFind(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ )
+/*++
+ Returns name of the the first/next BOOT structure supporting a given
+ VendorId.
+ The "first" BOOT does not have to be absolutely first, but first behind
+ the BOOT specified by RPL_FILTER.
+ In case such BOOT structure cannot be found, returns some kind of error.
+
+Arguments:
+
+ pFilter - pointer to RPL_FILTER structure with the following fields
+ FindFirst : find first (TRUE) or find next (FALSE)
+ VendorId : vendor id to be suppored (input only)
+ BootName : buffer large enough to hold the longest
+ BootName string. When input value of
+ BootNameSize is non-zero, this contains the
+ previous value of BootName string.
+ output value is NOT valid if error or end of table
+ BootNameSize : at input this contains the size of the previous
+ BootName string. If this size is zero/non-zero,
+ it means we are looking for the first/next
+ BOOT structure.
+ at output this cotains the size of BootName string
+ output value is NOT valid if error or end of table
+
+ pTableEnd - pointer to boolean (input value is FALSE) which will be set
+ to TRUE if we reach end of boot table
+-**/
+{
+ DWORD CheckVendorId;
+ DWORD DataSize;
+ JET_ERR JetError;
+
+ if ( pFilter->FindFirst == FALSE) {
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errNoCurrentRecord) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("JetError=%d", JetError));
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+ goto cleanup;
+ }
+ } else {
+ pFilter->FindFirst = FALSE;
+ CallM( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, &pFilter->VendorId, sizeof( pFilter->VendorId), JET_bitNewKey));
+ if ( pFilter->BootNameSize != 0) {
+ //
+ // We are continuing the search from the previous BOOT structure.
+ //
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, pFilter->BootName, pFilter->BootNameSize, 0));
+ }
+ //
+ // In case of seek errors assume we have reached end of table.
+ //
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootFilterFind: Seek(0x%x) => JetError=%d",
+ pFilter->VendorId, JetError));
+ goto cleanup;
+ }
+ //
+ // Verify that element we seeked to has the proper vendor id.
+ //
+ CallM( JetMakeKey( pSession->SesId, pSession->BootTableId, &pFilter->VendorId, sizeof( pFilter->VendorId), JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, pSession->BootTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootFilterFind: SetIndexRange(0x%x) => JetError=%d",
+ pFilter->VendorId, JetError));
+ goto cleanup;
+ }
+ }
+#ifdef RPL_DEBUG
+ CallM( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId, &CheckVendorId,
+ sizeof( CheckVendorId), &DataSize, 0, NULL));
+ if ( CheckVendorId != pFilter->VendorId) {
+ RplDump( ++RG_Assert, ( "CheckVendorId=0x%x"));
+ }
+#endif // RPL_DEBUG
+ CallM( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootName].ColumnId, pFilter->BootName,
+ RPL_MAX_BOOT_NAME_SIZE, &pFilter->BootNameSize, 0, NULL));
+cleanup:
+ if ( JetError < 0) {
+ *pTableEnd = TRUE; // pretend it is end of table
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD BootSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->WindowSize != -1) {
+ *pErrorParameter = BOOT_WindowSize;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId,
+ &Info->WindowSize, sizeof( Info->WindowSize), 0, NULL));
+ }
+ if ( Info->BbcFile != NULL) {
+ *pErrorParameter = BOOT_BbcFile;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId,
+ Info->BbcFile,
+ ( wcslen( Info->BbcFile) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->VendorName != NULL) {
+ DWORD VendorId;
+ *pErrorParameter = BOOT_VendorName;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorName].ColumnId,
+ Info->VendorName,
+ ( wcslen( Info->VendorName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ VendorId = wcstoul( Info->VendorName, NULL, 16);
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_VendorId].ColumnId,
+ &VendorId, sizeof( VendorId), 0, NULL));
+ }
+ if ( Info->Flags != 0) {
+ *pErrorParameter = BOOT_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->BootComment != NULL) {
+ *pErrorParameter = BOOT_BootComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootComment].ColumnId,
+ Info->BootComment,
+ ( wcslen( Info->BootComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = BOOT_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD BootGetInfo(
+ IN PRPL_SESSION pSession,
+ OUT PDWORD pVendorId,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ DWORD WhoCares;
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = BootGetField( pSession, BOOT_WindowSize, (LPVOID *)&Info->WindowSize, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_BbcFile, &Info->BbcFile, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = BootGetField( pSession, BOOT_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_VendorName, &Info->VendorName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = BootGetField( pSession, BOOT_BootComment, &Info->BootComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = BootGetField( pSession, BOOT_VendorId, (LPVOID *)pVendorId, &WhoCares);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID BootGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_BOOT_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ if ( Info->BbcFile != NULL) {
+ MIDL_user_free( Info->BbcFile);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->VendorName != NULL) {
+ MIDL_user_free( Info->VendorName);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->BootComment != NULL) {
+ MIDL_user_free( Info->BootComment);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_BOOT_INFO_STRUCT BootInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_BOOT_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = BootInfoStruct->BootInfo2;
+
+ switch( Level) {
+ case 2:
+ if ( Info->BbcFile == NULL || RPL_STRING_TOO_LONG( Info->BootComment)) {
+ ErrorParameter = BOOT_BbcFile;
+ break;
+ }
+ if ( !ValidHexName( Info->VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ ErrorParameter = BOOT_VendorName;
+ break;
+ }
+ _wcsupr( Info->VendorName);
+ VendorId = wcstoul( Info->VendorName, NULL, 16);
+ switch( Info->Flags) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ break;
+ default:
+ ErrorParameter = BOOT_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->BootComment)) {
+ ErrorParameter = BOOT_BootComment;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = BOOT_BootName;
+ break;
+ }
+ _wcsupr( Info->BootName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that BootName + VendorId is available in the database.
+ //
+ if ( BootFind( pSession, Info->BootName, VendorId)) {
+ Error = NERR_RplBootNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->BootTableId, JET_prepInsert));
+
+ Error = BootSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->BootTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+BOOL BootFind(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN DWORD VendorId
+ )
+{
+ JET_ERR JetError;
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName));
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, &VendorId, sizeof(VendorId), JET_bitNewKey));
+ CallB( JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, ( wcslen( BootName) + 1) * sizeof(WCHAR), 0));
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekEQ);
+ if ( JetError < 0) {
+#ifdef RPL_DEBUG
+ //
+ // JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR BootName,
+ IN LPWSTR VendorName
+ )
+/*++
+ If VendorName is NULL we should delete all records with input BootName.
+ But for now we insist on valid input for VendorName.
+--*/
+{
+ DWORD Error;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( BootName);
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ VendorId = wcstoul( VendorName, NULL, 16);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, CONFIG_TABLE_TAG,
+ CONFIG_INDEX_BootNameConfigName, BootName)) {
+ //
+ // We found a CONFIG record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( RplFindByField( pSession, PROFILE_TABLE_TAG,
+ PROFILE_INDEX_BootNameProfileName, BootName)) {
+ //
+ // We found a PROFILE record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( RplFindByField( pSession, WKSTA_TABLE_TAG,
+ WKSTA_INDEX_BootNameWkstaName, BootName)) {
+ //
+ // We found a WKSTA record which uses this BOOT.
+ //
+ Error = NERR_RplBootInUse;
+ goto cleanup;
+ }
+ if ( !BootFind( pSession, BootName, VendorId)) {
+ Error = NERR_RplBootNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->BootTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplBootEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN OUT LPRPL_BOOT_ENUM BootEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ DWORD VendorId;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( BootEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_2);
+ TypicalSize += 40 * sizeof( WCHAR); // typical size of BbcFile
+ NOTHING; // fall through
+ case 1:
+ if ( BootEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_1);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of VendorName
+ NOTHING; // fall through
+ case 0:
+ if ( BootEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_BOOT_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of BootComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, (
+ "BootEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ BootEnum->BootInfo.Level0->Buffer = (LPRPL_BOOT_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !BootResumeFirst( pSession, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = BootGetInfo( pSession, &VendorId, BootEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->BootTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ BootGetInfoCleanup( BootEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ BootGetInfoCleanup( BootEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_BOOT, ("BootEnum: EntriesRead = 0x%x", EntriesRead));
+
+ BootEnum->BootInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ BootEnum->BootInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ BootResumeSave( pSession, (DWORD)ServerHandle, VendorId,
+ ((LPRPL_BOOT_INFO_0)(Buffer-CoreSize))->BootName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/config.c b/private/net/svcdlls/rpl/server/config.c
new file mode 100644
index 000000000..698cb962e
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/config.c
@@ -0,0 +1,711 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ This module contains RPL config apis: ConfigEnum.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "boot.h"
+#include "profile.h"
+#define RPLCONFIG_ALLOCATE
+#include "config.h"
+#undef RPLCONFIG_ALLOCATE
+#include "setup.h" // IsConfigEnabled()
+
+
+
+DWORD ConfigSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ *pErrorParameter = CONFIG_FitPersonal;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_FitPersonal].ColumnId,
+ Info->FitPersonal,
+ ( wcslen( Info->FitPersonal) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitShared != NULL) {
+ *pErrorParameter = CONFIG_FitShared;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_FitShared].ColumnId,
+ Info->FitShared,
+ ( wcslen( Info->FitShared) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName4 != NULL) {
+ *pErrorParameter = CONFIG_DirName4;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName4].ColumnId,
+ Info->DirName4,
+ ( wcslen( Info->DirName4) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName3 != NULL) {
+ *pErrorParameter = CONFIG_DirName3;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName3].ColumnId,
+ Info->DirName3,
+ ( wcslen( Info->DirName3) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName2 != NULL) {
+ *pErrorParameter = CONFIG_DirName2;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName2].ColumnId,
+ Info->DirName2,
+ ( wcslen( Info->DirName2) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->DirName != NULL) {
+ *pErrorParameter = CONFIG_DirName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_DirName].ColumnId,
+ Info->DirName,
+ ( wcslen( Info->DirName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = CONFIG_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->Flags != 0) {
+ *pErrorParameter = CONFIG_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ConfigComment != NULL) {
+ *pErrorParameter = CONFIG_ConfigComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_ConfigComment].ColumnId,
+ Info->ConfigComment,
+ ( wcslen( Info->ConfigComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ConfigName != NULL) {
+ *pErrorParameter = CONFIG_ConfigName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_ConfigName].ColumnId,
+ Info->ConfigName,
+ ( wcslen( Info->ConfigName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD ConfigGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case CONFIG_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplConfigInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplConfigInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ RPL_RETURN( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize; // BUGBUG might become negative?
+ return( NO_ERROR);
+}
+
+
+DWORD ConfigGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ConfigName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = ConfigGetField( pSession, CONFIG_FitPersonal, &Info->FitPersonal, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_FitShared, &Info->FitShared, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName4, &Info->DirName4, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName3, &Info->DirName3, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName2, &Info->DirName2, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName, &Info->DirName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ConfigGetField( pSession, CONFIG_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = ConfigGetField( pSession, CONFIG_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ case 0:
+ Error = ConfigGetField( pSession, CONFIG_ConfigComment, &Info->ConfigComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( ConfigName == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_ConfigName, &Info->ConfigName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( ConfigName) + 1) * sizeof(WCHAR);
+ Info->ConfigName = MIDL_user_allocate( DataSize);
+ if ( Info->ConfigName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, ( "ConfigName=0x%x", Info->ConfigName));
+ memcpy( Info->ConfigName, ConfigName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID ConfigGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ MIDL_user_free( Info->FitPersonal);
+ }
+ if ( Info->FitShared != NULL) {
+ MIDL_user_free( Info->FitShared);
+ }
+ if ( Info->DirName4 != NULL) {
+ MIDL_user_free( Info->DirName4);
+ }
+ if ( Info->DirName3 != NULL) {
+ MIDL_user_free( Info->DirName3);
+ }
+ if ( Info->DirName2 != NULL) {
+ MIDL_user_free( Info->DirName2);
+ }
+ if ( Info->DirName != NULL) {
+ MIDL_user_free( Info->DirName);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ NOTHING; // fall through
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ConfigComment != NULL) {
+ MIDL_user_free( Info->ConfigComment);
+ }
+ if ( Info->ConfigName != NULL) {
+ MIDL_user_free( Info->ConfigName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_CONFIG_INFO_STRUCT ConfigInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_CONFIG_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = ConfigInfoStruct->ConfigInfo2;
+
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->DirName4, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName4;
+ break;
+ }
+ if ( !ValidName( Info->DirName3, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = CONFIG_DirName3;
+ break;
+ }
+ if ( !ValidName( Info->DirName2, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName2;
+ break;
+ }
+ if ( !ValidName( Info->DirName, RPL_MAX_STRING_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_DirName;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_BootName;
+ break;
+ }
+ _wcsupr( Info->BootName);
+ if ( Info->Flags & ~CONFIG_FLAGS_MASK_ENABLED) {
+ ErrorParameter = CONFIG_Flags;
+ break;
+ }
+ switch ( Info->Flags) {
+ case 0:
+ Info->Flags = RplConfigEnabled( Info->DirName2) == TRUE ?
+ CONFIG_FLAGS_ENABLED_TRUE : CONFIG_FLAGS_ENABLED_FALSE;
+ break;
+ case CONFIG_FLAGS_ENABLED_TRUE:
+ case CONFIG_FLAGS_ENABLED_FALSE:
+ break;
+ default:
+ ErrorParameter = CONFIG_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->ConfigComment)) {
+ ErrorParameter = CONFIG_ConfigComment;
+ break;
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = CONFIG_ConfigName;
+ break;
+ }
+ _wcsupr( Info->ConfigName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that ConfigName is available in the database.
+ //
+ if ( RplFind( pSession, CONFIG_TABLE_TAG, Info->ConfigName)) {
+ Error = NERR_RplConfigNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ConfigTableId, JET_prepInsert));
+
+ Error = ConfigSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->ConfigTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ConfigName
+ )
+/*++
+--*/
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ConfigName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, PROFILE_TABLE_TAG,
+ PROFILE_INDEX_ConfigNameProfileName, ConfigName)) {
+ //
+ // We found a PROFILE record which uses this CONFIG.
+ //
+ Error = NERR_RplConfigNotEmpty;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, CONFIG_TABLE_TAG, ConfigName)) {
+ Error = NERR_RplConfigNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->ConfigTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplConfigEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN PWCHAR AdapterName,
+ IN OUT LPRPL_CONFIG_ENUM ConfigEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ If AdapterName is null, enumerate all configurations.
+ If AdapterName is not null, enumerate all configurations that can support
+ this adapter id.
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ RPL_FILTER Filter;
+ PRPL_FILTER pFilter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( ConfigEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_2);
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitPersonal
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitShared
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName4
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName3
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName2
+ TypicalSize += 12 * sizeof( WCHAR); // typical size of DirName
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ NOTHING; // fall through
+ case 1:
+ if ( ConfigEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_1);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( ConfigEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_CONFIG_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of ConfigComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ConfigName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( AdapterName != NULL) {
+ pFilter = &Filter;
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ pFilter->VendorId = AdapterNameToVendorId( AdapterName);
+ pFilter->FindFirst = TRUE;
+ } else {
+ pFilter = NULL;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, (
+ "ConfigEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+ ConfigEnum->ConfigInfo.Level0->Buffer = (LPRPL_CONFIG_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, CONFIG_TABLE_TAG, pFilter, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = ConfigGetInfo( pSession, NULL, ConfigEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->ConfigTableId, pFilter, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ ConfigGetInfoCleanup( ConfigEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ ConfigGetInfoCleanup( ConfigEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_CONFIG, ("ConfigEnum: EntriesRead = 0x%x", EntriesRead));
+
+ ConfigEnum->ConfigInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ ConfigEnum->ConfigInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, pFilter,
+ ((LPRPL_CONFIG_INFO_0)(Buffer-CoreSize))->ConfigName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
diff --git a/private/net/svcdlls/rpl/server/database.c b/private/net/svcdlls/rpl/server/database.c
new file mode 100644
index 000000000..aa96fc767
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/database.c
@@ -0,0 +1,998 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ database.c
+
+Abstract:
+
+ Routines for non-api _access to the database. This includes initialization
+ and shutdown code on the database.
+
+ Exports:
+
+BOOL RplDbInit( VOID)
+VOID RplDbTerm( VOID)
+BOOL RplDbFindWksta(
+BOOL RplDbFillWksta(
+BOOL RplDbHaveWksta(
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "database.h"
+#include "db.h"
+#include "dblib.h"
+#include "adapter.h"
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "resume.h"
+#include "vendor.h"
+#include "wksta.h"
+#include "report.h" // for RplReportEventEx
+#include "winsock.h" // for INADDR_NONE
+
+DWORD RplStartJet500Conversion();
+
+#define RPL_BACKUP_SUBDIR L"BACKUP"
+
+//
+// File names must be complete, i.e. extensions are not optional. (This is unlike
+// the OS/2 behavior where we would append ".FIT" if extension is absent).
+//
+
+
+
+BOOL RplDbInitColumnInfo(
+ IN PCHAR TableName,
+ IN OUT PRPL_COLUMN_INFO ColumnInfoTable,
+ IN DWORD ColumnInfoTableLength,
+ IN JET_TABLEID TableId,
+ IN JET_SESID SesId
+ )
+{
+ JET_COLUMNDEF ColumnDef;
+ DWORD index;
+
+ for ( index = 0; index < ColumnInfoTableLength; index++) {
+ CallB( JetGetTableColumnInfo( SesId, TableId,
+ ColumnInfoTable[ index].ColumnName, &ColumnDef,
+ sizeof( ColumnDef), JET_ColInfo));
+ RPL_ASSERT( ColumnInfoTable[ index].ColumnType == ColumnDef.coltyp);
+ ColumnInfoTable[ index].ColumnId = ColumnDef.columnid;
+ }
+ return( TRUE);
+}
+
+
+//
+// Code templated from WINS:WinMscDelFiles(). JonN 8/7/94
+//
+VOID RplDeleteLogFiles()
+{
+ WCHAR Path[ MAX_PATH]; // must hold terminating NULL char too
+ WIN32_FIND_DATA FileInfo;
+ HANDLE SearchHandle = INVALID_HANDLE_VALUE;
+ DWORD ErrCode = NO_ERROR;
+
+ memcpy( Path, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( Path+RG_DirectoryLength, L"jet*.log", 9*sizeof(WCHAR));
+
+ SearchHandle = FindFirstFile(Path, &FileInfo);
+ if (SearchHandle == INVALID_HANDLE_VALUE)
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindFirstFile( %ws) returned %d",
+ Path, GetLastError()));
+ goto cleanup;
+ }
+
+ do {
+ memcpy( Path + RG_DirectoryLength,
+ FileInfo.cFileName,
+ (wcslen(FileInfo.cFileName)+1) * sizeof(WCHAR) );
+ if (!DeleteFile(Path))
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: DeleteFile( %ws) returned %d",
+ Path, GetLastError()));
+ goto cleanup;
+ }
+
+ } while(FindNextFile(SearchHandle, &FileInfo));
+ if ((ErrCode = GetLastError()) != ERROR_NO_MORE_FILES)
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindNextFile() returned %d",
+ ErrCode));
+
+ }
+
+cleanup:
+ if ( SearchHandle != INVALID_HANDLE_VALUE && !FindClose(SearchHandle))
+ {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDeleteLogFiles: FindClose() returned %d",
+ GetLastError()));
+ }
+
+ return;
+}
+
+
+BOOL RplDbInitPath(
+ IN OUT PWCHAR Path,
+ IN PWCHAR Name,
+ OUT PCHAR * pDbcsPath
+ )
+/*++
+
+Routine Description:
+ Allocates DBCS string corresponding to a UNICODE string obtained via
+ concatenation of Path & Name strings.
+
+Arguments:
+ Path - first UNICODE string
+ Name - second UNICODE string
+ pDbcsPath - pointer to a DBCS string corresponding to a concatenation
+ of above two UNICODE strings
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ DWORD NameLength;
+ DWORD PathLength; // not count terminating NULL char
+
+ NameLength = wcslen( Name);
+ PathLength = RG_DirectoryLength + NameLength;
+ if ( PathLength >= MAX_PATH) {
+ RPL_RETURN( FALSE);
+ }
+ memcpy( Path + RG_DirectoryLength, Name, (NameLength+1)*sizeof(WCHAR));
+ NameLength = RplUnicodeToDbcs( RG_MemoryHandle, Path, PathLength,
+ MAX_PATH * sizeof(WCHAR), pDbcsPath);
+ if ( NameLength == 0) {
+ RPL_RETURN( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbSessionInit(
+ IN BOOL MainSession,
+ OUT PRPL_SESSION pSession,
+ OUT BOOL * pErrorReported
+ )
+{
+
+ CallB( JetBeginSession( RG_Instance, &pSession->SesId, "admin", ""));
+ if ( MainSession) {
+ JET_ERR err = JetAttachDatabase( pSession->SesId, RG_Mdb, 0);
+ if ( err == JET_errSuccess ) {
+ //
+ // JonN 6/16/95 This code works around a JET bug in cases
+ // where the JET database has moved. The code fragment was
+ // suggested by Ian Jose. JetAttachDatabase will return
+ // JET_wrnDatabaseAttached under normal circumstances.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "RplDbSessionInit: JetAttachDatabase returned JET_errSuccess, engaging workaround" ));
+ // Detach(NULL) detached all databases, new to NT 3.51 and up
+ Call( JetDetachDatabase( pSession->SesId, NULL));
+ //
+ // It is OK if this call returns JET_errSuccess since
+ // we explicitly detached. Note that we do not normally
+ // detach at all.
+ //
+ CallB( JetAttachDatabase( pSession->SesId, RG_Mdb, 0));
+ }
+ else if ( err == JET_errDatabase200Format ) {
+ //
+ // JetInit will succeed if no 200-series logs exist, and the
+ // problem will not be caught until here.
+ //
+ DWORD converr;
+ converr = RplStartJet500Conversion();
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &converr);
+ *pErrorReported = TRUE;
+ return( FALSE);
+ }
+ else
+ {
+ CallB( err );
+ }
+ RG_DetachDatabase = TRUE;
+ }
+
+ CallB( JetOpenDatabase( pSession->SesId, RG_Mdb, NULL, &pSession->DbId, 0));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, ADAPTER_TABLE_NAME, NULL, 0,
+ 0, &pSession->AdapterTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, BOOT_TABLE_NAME, NULL, 0,
+ 0, &pSession->BootTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, CONFIG_TABLE_NAME, NULL, 0,
+ 0, &pSession->ConfigTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, PROFILE_TABLE_NAME, NULL, 0,
+ 0, &pSession->ProfileTableId));
+ if ( MainSession) {
+ DWORD Error;
+ Error = ResumeCreateTable( pSession); // initializes ResumeTable also
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error=%d", Error));
+ return( FALSE);
+ }
+ }
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME, NULL, 0,
+ 0, &pSession->ResumeTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, VENDOR_TABLE_NAME, NULL, 0,
+ 0, &pSession->VendorTableId));
+ CallB( JetOpenTable( pSession->SesId, pSession->DbId, WKSTA_TABLE_NAME, NULL, 0,
+ 0, &pSession->WkstaTableId));
+
+ if ( MainSession) {
+ if ( !RplDbInitColumnInfo( ADAPTER_TABLE_NAME, AdapterTable,
+ ADAPTER_TABLE_LENGTH, pSession->AdapterTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( BOOT_TABLE_NAME, BootTable,
+ BOOT_TABLE_LENGTH, pSession->BootTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( CONFIG_TABLE_NAME, ConfigTable,
+ CONFIG_TABLE_LENGTH, pSession->ConfigTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( PROFILE_TABLE_NAME, ProfileTable,
+ PROFILE_TABLE_LENGTH, pSession->ProfileTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ //
+ // ColumnInfo for resume table has been initialized already.
+ //
+ if ( !RplDbInitColumnInfo( VENDOR_TABLE_NAME, VendorTable,
+ VENDOR_TABLE_LENGTH, pSession->VendorTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ if ( !RplDbInitColumnInfo( WKSTA_TABLE_NAME, WkstaTable,
+ WKSTA_TABLE_LENGTH, pSession->WkstaTableId, pSession->SesId)) {
+ return( FALSE);
+ }
+ }
+ return( TRUE);
+}
+
+
+VOID RplDbSessionTerm(
+ IN BOOL MainSession,
+ IN PRPL_SESSION pSession
+ )
+{
+ if ( pSession->AdapterTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->AdapterTableId));
+ }
+ if ( pSession->BootTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->BootTableId));
+ }
+ if ( pSession->ConfigTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ConfigTableId));
+ }
+ if ( pSession->ProfileTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ProfileTableId));
+ }
+ if ( pSession->ResumeTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ if ( MainSession) {
+ Call( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ }
+ }
+ if ( pSession->VendorTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->VendorTableId));
+ }
+ if ( pSession->WkstaTableId != 0) {
+ Call( JetCloseTable( pSession->SesId, pSession->WkstaTableId));
+ }
+ if ( pSession->DbId != 0) {
+ Call( JetCloseDatabase( pSession->SesId, pSession->DbId, 0));
+ }
+ if ( pSession->SesId != 0) {
+ if ( MainSession && RG_DetachDatabase) {
+#if 0
+ //
+ // Because of JET restore bugs we are advised NOT TO
+ // detach database ever.
+ //
+ Call( JetDetachDatabase( pSession->SesId, RG_Mdb));
+#endif
+ RG_DetachDatabase = FALSE;
+ }
+ Call( JetEndSession( pSession->SesId, 0));
+ }
+}
+
+
+BOOL RplDbFindBoot(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN PWCHAR AdapterName
+ )
+/*++
+ Return TRUE if it finds server record for input BootName & AdapterName.
+ Returns FALSE otherwise.
+
+ Same comment as for RplDbFindWksta.
+--*/
+{
+ JET_ERR JetError;
+ DWORD Vendor;
+ WCHAR SaveChar;
+
+ JetError = JetSetCurrentIndex( pSession->SesId, pSession->BootTableId, BOOT_INDEX_VendorIdBootName);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ SaveChar = AdapterName[ RPL_VENDOR_NAME_LENGTH];
+ AdapterName[ RPL_VENDOR_NAME_LENGTH] = 0;
+ Vendor = wcstoul( AdapterName, NULL, 16);
+ AdapterName[ RPL_VENDOR_NAME_LENGTH] = SaveChar;
+ JetError = JetMakeKey( pSession->SesId, pSession->BootTableId, &Vendor, sizeof( Vendor), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( pSession->SesId, pSession->BootTableId, BootName, ( wcslen( BootName) + 1) * sizeof(WCHAR), 0);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( pSession->SesId, pSession->BootTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+ if ( JetError == JET_errRecordNotFound) {
+ //
+ // This is an expected error, do not break for this.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "DbFindWksta( %ws) failed", AdapterName));
+ } else {
+ RplDump( ++RG_Assert, ("JetSeek failed err=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbAddAdapterName(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ )
+/*++
+ Try to add the adapter record for input AdapterName.
+
+ CODEWORK Should use different comments for different VendorName-s.
+
+Return Value:
+ TRUE if adapter record for input AdapterName was added
+ FALSE otherwise
+
+--*/
+{
+#define ADAPTER_GENERIC_COMMENT L"An unknown client network adapter id."
+ JET_ERR JetError;
+ DWORD Flags = 0;
+ BYTE LocalBuffer[ 300];
+ DWORD DataSize;
+ PWCHAR AdapterComment;
+ WCHAR VendorName[ RPL_VENDOR_NAME_LENGTH + 1];
+
+ if ( RplFind( pSession, ADAPTER_TABLE_TAG, AdapterName)) {
+ return( FALSE); // adapter record is already present
+ }
+
+ memcpy( VendorName, AdapterName, RPL_VENDOR_NAME_LENGTH*sizeof(WCHAR));
+ VendorName[ RPL_VENDOR_NAME_LENGTH] = 0;
+
+ if ( RplFind( pSession, VENDOR_TABLE_TAG, VendorName)) {
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId, LocalBuffer,
+ sizeof( LocalBuffer), &DataSize, 0, NULL));
+ if ( DataSize > sizeof( LocalBuffer)) {
+ AdapterComment = ADAPTER_GENERIC_COMMENT;
+ } else {
+ AdapterComment = (PWCHAR)LocalBuffer;
+ }
+ } else {
+ AdapterComment = ADAPTER_GENERIC_COMMENT;
+ }
+
+ CallB( JetPrepareUpdate( pSession->SesId, pSession->AdapterTableId,
+ JET_prepInsert));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterName].ColumnId, AdapterName,
+ ( wcslen( AdapterName) + 1) * sizeof(WCHAR), 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_AdapterComment].ColumnId, AdapterComment,
+ ( wcslen( AdapterComment) + 1) * sizeof(WCHAR), 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->AdapterTableId,
+ AdapterTable[ ADAPTER_Flags].ColumnId, &Flags,
+ sizeof(Flags), 0, NULL));
+ JetError = JetUpdate( pSession->SesId, pSession->AdapterTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError != JET_errKeyDuplicate) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ }
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+VOID RplDbInstanceTerm( VOID)
+{
+ if ( !RG_InstanceAllocated) {
+ return;
+ }
+ RplDbSessionTerm( FALSE, &RG_ApiSession);
+ RplDbSessionTerm( FALSE, &RG_WorkerSession);
+ RplDbSessionTerm( TRUE, &RG_RequestSession);
+ Call( JetTerm2( RG_Instance, JET_bitTermComplete));
+ RG_InstanceAllocated = FALSE;
+}
+
+
+BOOL RplDbInstanceInit(
+ PCHAR SystemMdb,
+ PCHAR TempMdb,
+ PCHAR LogFilePath,
+ BOOL * pErrorReported
+ )
+{
+ RG_InstanceAllocated = FALSE;
+ RG_DetachDatabase = FALSE;
+ memset( &RG_RequestSession, 0, sizeof( RG_RequestSession));
+ memset( &RG_WorkerSession, 0, sizeof( RG_WorkerSession));
+ memset( &RG_ApiSession, 0, sizeof( RG_ApiSession));
+
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramSystemPath, 0, LogFilePath));
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramSysDbPath, 0, SystemMdb));
+#endif
+ RG_InstanceAllocated = TRUE;
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramTempPath, 0, TempMdb));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFilePath, 0, LogFilePath));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxBuffers, 250, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBfThrshldLowPrcnt, 0, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBfThrshldHighPrcnt, 100, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxOpenTables, 30, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxOpenTableIndexes, 105, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxCursors, 100, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxSessions, 10, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxVerPages, 64, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramMaxTemporaryTables, 5, NULL));
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogBuffers, 41, NULL));
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFileSize, 1000, NULL));
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFileSectors, 1000, NULL));
+#endif
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramLogFlushThreshold, 10, NULL));
+#ifdef __JET500
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramBaseName, 0, "j50"));
+ {
+ JET_ERR JetError = JetInit( &RG_Instance);
+ //
+ // JetInit will fail if 200-series logs exist.
+ //
+ if ( JetError == JET_errDatabase200Format ) {
+ DWORD converr;
+ converr = RplStartJet500Conversion();
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &converr);
+ *pErrorReported = TRUE;
+ return( FALSE);
+ }
+ else
+ {
+ CallB( JetError );
+ }
+ }
+#else
+ CallB( JetSetSystemParameter( &RG_Instance, 0, JET_paramRecovery, 0, "on"));
+ CallB( JetInit( &RG_Instance));
+#endif
+
+ if ( !RplDbSessionInit( TRUE, &RG_RequestSession, pErrorReported)) {
+ return( FALSE);
+ }
+ if ( !RplDbSessionInit( FALSE, &RG_WorkerSession, pErrorReported)) {
+ return( FALSE);
+ }
+ if ( !RplDbSessionInit( FALSE, &RG_ApiSession, pErrorReported)) {
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL RplDbInit()
+{
+ WCHAR Path[ MAX_PATH]; // must hold terminating NULL char too
+ JET_ERR JetError;
+ PCHAR SystemMdb = NULL; // needed for cleanup below
+ PCHAR TempMdb = NULL; // needed for cleanup below
+ PCHAR LogFilePath = NULL; // needed for cleanup below
+ BOOL Success = FALSE;
+ BOOL ErrorReported = FALSE;
+
+ memcpy( Path, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+
+ if ( !RplDbInitPath( Path, RPL_BACKUP_SUBDIR, &RG_BackupPath)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_SERVICE_DATABASE_W, &RG_Mdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_SYSTEM_DATABASE_W, &SystemMdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, RPL_TEMP_DATABASE_W, &TempMdb)) {
+ goto cleanup;
+ }
+ if ( !RplDbInitPath( Path, L"", &LogFilePath)) {
+ goto cleanup;
+ }
+
+#if 1
+ if ( !RplDbInstanceInit( SystemMdb, TempMdb, LogFilePath, &ErrorReported)) {
+ if (ErrorReported) {
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplInitDatabase, NULL, 0, NULL);
+ RplDbInstanceTerm();
+#else
+ //
+ // Used only for testing purposes (to restore without attempt to init first)
+ //
+ if ( TRUE) {
+#endif
+#ifdef __JET500
+ JetError = JetRestore( RG_BackupPath, 0);
+#else
+ JetError = JetRestore( RG_BackupPath, 0, NULL, 0);
+#endif
+ if ( JetError == JET_errBadLogVersion
+//
+// JonN 11/28/95 According to JLiem, the old BadNextLogVersion is broken up
+// into two errors and two warnings in JET500. If we get either of the
+// warnings (558 or 559) then JET took care of deleting the old log files
+// and we can continue. We handle the errors as we handled BadNextLogVersion.
+//
+#ifdef __JET500
+ || JetError == JET_errGivenLogFileHasBadSignature
+ || JetError == JET_errGivenLogFileIsNotContiguous
+#else
+ || JetError == JET_errBadNextLogVersion
+#endif
+ ) {
+ RplDeleteLogFiles();
+#ifdef __JET500
+ JetError = JetRestore( RG_BackupPath, 0);
+#else
+ JetError = JetRestore( RG_BackupPath, 0, NULL, 0);
+#endif
+ }
+#ifdef __JET500
+#ifndef JET_ATTACH_CATCHES_ERROR
+ if ( JetError == JET_errDatabase200Format ) {
+ RplReportEvent( NELOG_RplUpgradeDBTo40, NULL, sizeof(DWORD), &JetError);
+ RplDump( ++RG_Assert,( "200-fmt from JetRestore" ));
+ goto cleanup;
+ }
+#endif
+#endif
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetRestore( %s) failed err=%d", RG_BackupPath, JetError));
+ RplReportEvent( NELOG_RplRestoreDatabaseFailure, NULL, sizeof(DWORD), &JetError);
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplRestoreDatabaseSuccess, NULL, 0, NULL);
+ if ( !RplDbInstanceInit( SystemMdb, TempMdb, LogFilePath, &ErrorReported)) {
+ if (ErrorReported) {
+ goto cleanup;
+ }
+ RplReportEvent( NELOG_RplInitRestoredDatabase, NULL, 0, NULL);
+ goto cleanup;
+ }
+ }
+ Success = TRUE;
+
+cleanup:
+ if ( SystemMdb != NULL) {
+ RplMemFree( RG_MemoryHandle, SystemMdb);
+ }
+ if ( TempMdb != NULL) {
+ RplMemFree( RG_MemoryHandle, TempMdb);
+ }
+ if ( LogFilePath != NULL) {
+ RplMemFree( RG_MemoryHandle, LogFilePath);
+ }
+ return( Success);
+}
+
+
+VOID RplDbTerm( VOID)
+/*++
+
+ Save changes that may have been made to the database. Without this
+ the database may be left in an unusable state where any subsequent
+ attempt of calling JetInit() for this database would fail.
+
+--*/
+{
+ RplDbInstanceTerm();
+ RplMemFree( RG_MemoryHandle, RG_Mdb);
+ RplMemFree( RG_MemoryHandle, RG_BackupPath);
+}
+
+
+BOOL RplDbFindWksta(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ )
+/*++
+ Return TRUE if it finds wksta record for input AdapterName.
+ Returns FALSE otherwise.
+
+ This code could be make more efficient by defining AdapterName
+ to be jet currency data (it is silly now taking wcslen of
+ AdapterName since it is a fixed length string.
+
+ It is ASSUMED that the caller of this function will ensure transaction
+ processing.
+
+--*/
+{
+ JET_ERR JetError;
+
+ JetError = JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_AdapterName);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("SetCurrentIndex failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetMakeKey( pSession->SesId, pSession->WkstaTableId, AdapterName, ( wcslen( AdapterName) + 1) * sizeof(WCHAR), JET_bitNewKey);
+ if ( JetError != JET_errSuccess) {
+ RplDump( ++RG_Assert, ("MakeKey failed err=%d", JetError));
+ return( FALSE);
+ }
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekEQ);
+ if ( JetError != JET_errSuccess) {
+#ifdef RPL_DEBUG
+ //
+ // Do not assert for expected errors ( empty table or
+ // failure to find a record).
+ //
+ if ( JetError == JET_errNoCurrentRecord
+ || JetError == JET_errRecordNotFound) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, (
+ "DbFindWksta( %ws) failed", AdapterName));
+ } else {
+ RplDump( ++RG_Assert, ("JetSeek failed err=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplDbFillWksta(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_WORKER_DATA pWorkerData
+ )
+/*++
+ Returns TRUE if it can find all the information needed to boot the client.
+ Returns FALSE otherwise.
+
+ This routine should be modified to use ConfigGetInfo() - but without the
+ penalty of memory allocations.
+--*/
+{
+ DWORD DataSize;
+ PWCHAR AdapterName;
+ WCHAR BootName[ RPL_MAX_BOOT_NAME_LENGTH + 1];
+ WCHAR FilePath[ MAX_PATH];
+ DWORD Length;
+ DWORD Flags;
+
+ AdapterName = pWorkerData->pRcb->AdapterName;
+
+ if ( !RplDbFindWksta( pSession, AdapterName)) {
+ RplDump( ++RG_Assert, ("FindWksta( %ws) failed", AdapterName));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NERR_RplWkstaNotFound;
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId, pWorkerData->WkstaName,
+ sizeof( pWorkerData->WkstaName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, pWorkerData->ProfileName,
+ sizeof( pWorkerData->ProfileName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId, FilePath,
+ sizeof( FilePath), &DataSize, 0, NULL));
+ Length = DataSize / sizeof( WCHAR) - 1;
+ if ( DataSize > sizeof( FilePath) || FilePath[ Length] != 0) {
+ RplDump( ++RG_Assert, ( "FitFile is bad %ws", FilePath));
+ return( FALSE);
+ }
+ pWorkerData->FitFile = RplMemAlloc( pWorkerData->MemoryHandle,
+ RG_DirectoryLength * sizeof(WCHAR) + DataSize);
+ if ( pWorkerData->FitFile == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->FitFile, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( pWorkerData->FitFile + RG_DirectoryLength, FilePath, DataSize);
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId, BootName,
+ sizeof( BootName), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId, &Flags,
+ sizeof( Flags), &DataSize, 0, NULL));
+
+ switch ( Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_REQUIRED;
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_OPTIONAL;
+ break;
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ pWorkerData->LogonInput = WKSTA_LOGON_INPUT_IMPOSSIBLE;
+ break;
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+ }
+ switch ( Flags & WKSTA_FLAGS_MASK_DHCP) {
+ default:
+ case WKSTA_FLAGS_DHCP_TRUE:
+ pWorkerData->TcpIpAddress = INADDR_NONE;
+ pWorkerData->TcpIpSubnet = INADDR_NONE;
+ pWorkerData->TcpIpGateway = INADDR_NONE;
+ pWorkerData->DisableDhcp = WKSTA_DISABLE_DHCP_FALSE;
+ break;
+ case WKSTA_FLAGS_DHCP_FALSE:
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &pWorkerData->TcpIpAddress,
+ sizeof( pWorkerData->TcpIpAddress), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &pWorkerData->TcpIpSubnet,
+ sizeof( pWorkerData->TcpIpSubnet), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &pWorkerData->TcpIpGateway,
+ sizeof( pWorkerData->TcpIpGateway), &DataSize, 0, NULL));
+ pWorkerData->DisableDhcp = WKSTA_DISABLE_DHCP_TRUE;
+ break;
+#if 0 // to help testing with old style records this is commented out
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+#endif
+ }
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId, &pWorkerData->TcpIpAddress,
+ sizeof( pWorkerData->TcpIpAddress), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId, &pWorkerData->TcpIpSubnet,
+ sizeof( pWorkerData->TcpIpSubnet), &DataSize, 0, NULL));
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId, &pWorkerData->TcpIpGateway,
+ sizeof( pWorkerData->TcpIpGateway), &DataSize, 0, NULL));
+
+ if ( !RplDbFindBoot( pSession, BootName, AdapterName)) {
+ RplDump( ++RG_Assert, ("FindBoot failed"));
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_BbcFile].ColumnId, FilePath,
+ sizeof( FilePath), &DataSize, 0, NULL));
+ Length = DataSize / sizeof( WCHAR) - 1;
+ if ( DataSize > sizeof( FilePath) || FilePath[ Length] != 0) {
+ RplDump( ++RG_Assert, ( "BbcFile is bad %ws", FilePath));
+ return( FALSE);
+ }
+ pWorkerData->BbcFile = RplMemAlloc( pWorkerData->MemoryHandle,
+ RG_DirectoryLength * sizeof(WCHAR) + DataSize);
+ if ( pWorkerData->BbcFile == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->BbcFile, RG_Directory, RG_DirectoryLength * sizeof(WCHAR));
+ memcpy( pWorkerData->BbcFile + RG_DirectoryLength, FilePath, DataSize);
+
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_WindowSize].ColumnId, &pWorkerData->WindowSize,
+ sizeof( pWorkerData->WindowSize), &DataSize, 0, NULL));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->BootTableId,
+ BootTable[ BOOT_Flags].ColumnId, &Flags,
+ sizeof( Flags), &DataSize, 0, NULL));
+ switch( Flags & BOOT_FLAGS_MASK_FINAL_ACKNOWLEDGMENT) {
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_TRUE:
+ pWorkerData->FinalAck = TRUE;
+ break;
+ case BOOT_FLAGS_FINAL_ACKNOWLEDGMENT_FALSE:
+ pWorkerData->FinalAck = FALSE;
+ break;
+ default:
+ RplDump( ++RG_Assert, ("Flags=0x%x", Flags));
+ return( FALSE);
+ break;
+ }
+ return( TRUE);
+}
+
+
+BOOL RplWorkerFillWksta( IN OUT PRPL_WORKER_DATA pWorkerData)
+{
+ PRPL_SESSION pSession = &RG_WorkerSession;
+ BOOL Success;
+
+ EnterCriticalSection( &RG_ProtectWorkerSession);
+ Call( JetBeginTransaction( pSession->SesId));
+ Success = RplDbFillWksta( pSession, pWorkerData);
+ JetCommitTransaction( pSession->SesId, 0);
+ LeaveCriticalSection( &RG_ProtectWorkerSession);
+ return( Success);
+}
+
+
+BOOL RplRequestHaveWksta( IN LPWSTR AdapterName)
+/*++
+
+Routine Description:
+
+ If it finds wksta record for input AdapterName then it returns TRUE.
+ Else, it tries to add the adapter record for input AdapterName, then
+ returns FALSE.
+
+ This code could be make more efficient by defining AdapterName
+ to be jet currency data (it is silly now taking wcslen of
+ AdapterName since it is a fixed length string.
+
+Return Value:
+ TRUE if wksta record for input AdapterName is found
+ FALSE otherwise
+
+--*/
+{
+ PRPL_SESSION pSession = &RG_RequestSession;
+ BOOL WkstaFound;
+ BOOL AdapterAdded;
+
+ EnterCriticalSection( &RG_ProtectRequestSession);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ WkstaFound = RplDbFindWksta( pSession, AdapterName);
+
+ if ( !WkstaFound) {
+ //
+ // Failed to find it. Try to add adapter record then.
+ //
+ AdapterAdded = RplDbAddAdapterName( pSession, AdapterName);
+ } else {
+ AdapterAdded = FALSE;
+ }
+
+ if ( AdapterAdded) {
+ //
+ // We do not flush newly added adapter records since we do
+ // not want to slow down request threads.
+ //
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectRequestSession);
+ return( WkstaFound);
+}
+
+
+//
+// Trmplated from DHCP's database.c 12/13/95 JonN
+//
+DWORD
+RplStartJet500Conversion(
+ )
+/*++
+
+Routine Description:
+
+ This function starts the process to convert the jet200 version
+ database to jet500 version database. The Dhcp will terminate
+ after starting this process. When the conversion completes,
+ the dhcp service would be restarted by the convert process itself.
+
+Arguments:
+
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ DWORD ExLen;
+ STARTUPINFOA StartupInfo = {0};
+ PROCESS_INFORMATION ProcessInfo = {0};
+ CHAR szCmdLine[MAX_PATH];
+
+#define JET_CONV_MODULE_NAME "%SystemRoot%\\system32\\jetconv REMOTEBOOT /@"
+
+ ExLen = ExpandEnvironmentStringsA( JET_CONV_MODULE_NAME, szCmdLine, MAX_PATH );
+
+ if( (ExLen == 0) || (ExLen > MAX_PATH) ) {
+
+ if( ExLen == 0 ) {
+ return GetLastError();
+ }
+ else {
+ return ERROR_META_EXPANSION_TOO_LONG;
+ }
+
+ }
+
+
+ StartupInfo.cb = sizeof(STARTUPINFOA);
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST, ( "Calling %s\n",szCmdLine ));
+
+ if ( !CreateProcessA(
+ NULL,
+ szCmdLine,
+ NULL,
+ NULL,
+ FALSE,
+ DETACHED_PROCESS,
+// CREATE_NEW_CONSOLE,
+ NULL,
+ NULL,
+ &StartupInfo,
+ &ProcessInfo)
+ ) {
+
+ return GetLastError();
+
+
+ }
+
+ return ERROR_SUCCESS;
+}
diff --git a/private/net/svcdlls/rpl/server/database.h b/private/net/svcdlls/rpl/server/database.h
new file mode 100644
index 000000000..91811d5ff
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/database.h
@@ -0,0 +1,33 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ database.h
+
+Abstract:
+
+ Exports from database.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplDbInit( VOID);
+VOID RplDbTerm( VOID);
+BOOL RplDbFindWksta(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR AdapterName
+ );
+BOOL RplWorkerFillWksta( IN OUT PRPL_WORKER_DATA pWorkerData);
+BOOL RplRequestHaveWksta( IN LPWSTR AdapterName);
+
diff --git a/private/net/svcdlls/rpl/server/db.h b/private/net/svcdlls/rpl/server/db.h
new file mode 100644
index 000000000..572dc9f6b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/db.h
@@ -0,0 +1,263 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ db.h
+
+Abstract:
+
+ Main include file for JET database portion of RPL service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+//
+// jet call macros:
+// Call - jet call ignore error
+// CallR - jet call RETURN on error
+// CallB - jet call return BOOLEAN (false) on error
+// CallM - jet call return MAPPED error
+//
+
+#ifdef RPL_DEBUG
+#define Call( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ } \
+ }
+
+#define CallR( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return; \
+ } \
+ } \
+ }
+
+#define CallB( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return( FALSE); \
+ } \
+ } \
+ }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError != JET_errSuccess) { \
+ if ( _JetError < 0) { \
+ ++RG_Assert; \
+ } \
+ RplDebugPrint("File = %s, Line = %d, _JetError = %d\n", __FILE__, __LINE__, _JetError); \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ } \
+ }
+
+#else
+#define Call( fn ) { if ( fn < 0) { NOTHING;} }
+#define CallR( fn ) { if ( fn < 0) { return;} }
+#define CallB( fn ) { if ( fn < 0) { return( FALSE);} }
+#define CallM( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ return( NERR_RplInternal); \
+ } \
+ }
+#define CallJ( fn ) \
+ { \
+ int _JetError = fn; \
+ if ( _JetError < 0) { \
+ Error = NERR_RplInternal; \
+ goto cleanup; \
+ } \
+ }
+#endif
+
+
+#define DEFAULT_BUFFER_SIZE (64 * 1024) // arbitrary choice
+
+typedef enum _RPL_TABLE_TAG {
+ ADAPTER_TABLE_TAG = 0,
+ BOOT_TABLE_TAG,
+ CONFIG_TABLE_TAG,
+ PROFILE_TABLE_TAG,
+ RESUME_TABLE_TAG,
+ VENDOR_TABLE_TAG,
+ WKSTA_TABLE_TAG
+} RPL_TABLE_TAG, *PRPL_TABLE_TAG;
+
+typedef struct _RPL_FILTER {
+ BOOL FindFirst;
+ DWORD VendorId;
+ DWORD BootNameSize;
+ WCHAR BootName[ 16];
+} RPL_FILTER, *PRPL_FILTER;
+
+
+//
+// Exports from resume.c
+//
+DWORD ResumeCreateTable( OUT PRPL_SESSION pSession);
+BOOL ResumeKeyGet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ResumeHandle,
+ OUT PVOID ResumeValue,
+ IN OUT PDWORD pResumeSize
+ );
+BOOL ResumeKeySet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PVOID ResumeValue,
+ IN DWORD ResumeSize,
+ OUT PDWORD pResumeHandle
+ );
+VOID ResumePrune(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle
+ );
+
+//
+// Exports from boot.c
+//
+DWORD BootFilterFind(
+ IN PRPL_SESSION pSession,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ );
+BOOL BootFind(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR BootName,
+ IN DWORD VendorId
+ );
+
+//
+// Exports from config.c
+//
+DWORD ConfigGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ );
+
+DWORD ConfigSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ );
+
+//
+// Exports from profile.c
+//
+DWORD ProfileGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ OUT LPINT pDataSize
+ );
+
+//
+// Exports from wksta.c
+//
+DWORD WkstaGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ );
+
+BOOL WkstaFindFirst(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR ProfileName
+ );
+
+//
+// Exports from disk.c
+//
+#define ADD_NEW_BRANCHES 0
+#define DEL_NEW_BRANCHES 1
+#define DEL_OLD_BRANCHES 2
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+DWORD WkstaDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing
+ );
+DWORD WkstaDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceWkstaName,
+ IN PWCHAR TargetWkstaName
+ );
+DWORD WkstaDiskSet(
+ IN DWORD Action,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing,
+ IN PWCHAR TargetWkstaName,
+ IN PWCHAR TargetProfileName,
+ IN DWORD TargetSharing
+ );
+DWORD ProfileDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR ProfileName,
+ IN PWCHAR DirName,
+ IN PWCHAR DirName2,
+ IN PWCHAR DirName3,
+ IN PWCHAR DirName4
+ );
+DWORD ProfileDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceProfileName,
+ IN PWCHAR TargetProfileName
+ );
+
diff --git a/private/net/svcdlls/rpl/server/dblib.c b/private/net/svcdlls/rpl/server/dblib.c
new file mode 100644
index 000000000..b95f88f90
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/dblib.c
@@ -0,0 +1,386 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dblib.c
+
+Abstract:
+
+ Common api worker routines.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "adapter.h"
+#include "boot.h"
+#include "config.h"
+#include "profile.h"
+#include "vendor.h"
+#include "wksta.h"
+#include "dblib.h"
+
+
+BOOL RplScan(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN BOOL FindNextBoot,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the next profile or config.
+--*/
+{
+ DWORD Error;
+ JET_ERR JetError;
+
+ for ( ; ;) {
+ if ( FindNextBoot == TRUE) {
+ Error = BootFilterFind( pSession, pFilter, pTableEnd);
+ if ( Error != NO_ERROR) {
+ return( FALSE);
+ }
+ if ( *pTableEnd == TRUE) {
+ return( TRUE);
+ }
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ }
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ }
+ //
+ // Verify that current record has the desired boot block & at the
+ // same time set index range to be used with JetMove( Next).
+ //
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, TableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError == JET_errSuccess) {
+ return( TRUE);
+ }
+ //
+ // There are no records for current BootName.
+ // Get next BootName for VendorId.
+ //
+ FindNextBoot = TRUE;
+ }
+}
+
+
+BOOL RplFilterFirst(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN OUT PRPL_FILTER pFilter OPTIONAL,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first record for given vendor id, following
+ the record described by the resume handle.
+--*/
+{
+#define MAX_RESUME_VALUE_SIZE \
+ (RPL_MAX_BOOT_NAME_SIZE + \
+ max( RPL_MAX_PROFILE_NAME_SIZE, RPL_MAX_CONFIG_NAME_SIZE))
+#define MAX_NAME_LENGTH \
+ (JETBUG_STRING_LENGTH + \
+ max( RPL_MAX_PROFILE_NAME_LENGTH, RPL_MAX_CONFIG_NAME_LENGTH))
+ BYTE ResumeValue[ MAX_RESUME_VALUE_SIZE];
+ DWORD ResumeSize;
+ DWORD Error;
+ JET_ERR JetError;
+ DWORD NameSize;
+ WCHAR Name[ MAX_NAME_LENGTH + 1];
+ PCHAR Index;
+ JET_TABLEID TableId;
+
+ *pTableEnd = FALSE;
+
+ switch ( TableTag) {
+ case ADAPTER_TABLE_TAG:
+ TableId = pSession->AdapterTableId;
+ Index = ADAPTER_INDEX_AdapterName;
+ break;
+ case BOOT_TABLE_TAG:
+ TableId = pSession->BootTableId;
+ Index = BOOT_INDEX_BootName;
+ break;
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ Index = pFilter == NULL ? CONFIG_INDEX_ConfigName : CONFIG_INDEX_BootNameConfigName;
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ Index = pFilter == NULL ? PROFILE_INDEX_ProfileName : PROFILE_INDEX_BootNameProfileName;
+ break;
+ case VENDOR_TABLE_TAG:
+ TableId = pSession->VendorTableId;
+ Index = VENDOR_INDEX_VendorName;
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ Index = WKSTA_INDEX_WkstaName;
+ break;
+ default:
+ RPL_ASSERT( FALSE);
+ }
+
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, Index));
+
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, TableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+
+ if ( (ARGUMENT_PRESENT( pResumeHandle)) && *pResumeHandle != 0) {
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ if ( pFilter == NULL) {
+ CallB( JetMakeKey( pSession->SesId, TableId, ResumeValue, ResumeSize, JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, TableId, JET_bitSeekGT));
+ return( TRUE);
+ }
+ pFilter->BootNameSize = (wcslen( (PWCHAR)ResumeValue) + 1) * sizeof(WCHAR);
+ memcpy( pFilter->BootName, ResumeValue, pFilter->BootNameSize);
+ NameSize = (wcslen( (PWCHAR)(ResumeValue + pFilter->BootNameSize)) + 1)
+ * sizeof(WCHAR);
+ memcpy( Name, ResumeValue + pFilter->BootNameSize, NameSize);
+ RPL_ASSERT( ResumeSize == pFilter->BootNameSize + NameSize);
+ } else {
+ if ( pFilter == NULL) {
+ return( TRUE);
+ }
+ pFilter->BootNameSize = 0; // scan from the beginning
+ Error = BootFilterFind( pSession, pFilter, pTableEnd);
+ if ( Error != NO_ERROR) {
+ return( FALSE);
+ }
+ if ( *pTableEnd == TRUE) {
+ return( TRUE);
+ }
+ NameSize = 0;
+ }
+ CallB( JetMakeKey( pSession->SesId, TableId, pFilter->BootName, pFilter->BootNameSize, JET_bitNewKey));
+ if ( NameSize != 0) {
+#ifdef RPL_JETBUG
+ memcpy( (PBYTE)Name + NameSize, JETBUG_STRING, JETBUG_STRING_SIZE);
+ NameSize += JETBUG_STRING_LENGTH * sizeof(WCHAR);
+#endif
+ CallB( JetMakeKey( pSession->SesId, TableId, Name, NameSize, 0));
+ }
+ return( RplScan( pSession, TableId, FALSE, pFilter, pTableEnd));
+}
+
+
+VOID RplFilterSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PRPL_FILTER pFilter,
+ IN PWCHAR Name,
+ IN PDWORD pResumeHandle
+ )
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)]; // BUGBUG hardcoding
+ DWORD ResumeSize;
+ DWORD NameSize;
+ if ( pFilter != NULL) {
+ ResumeSize = pFilter->BootNameSize;
+ memcpy( ResumeBuffer, pFilter->BootName, ResumeSize);
+ } else {
+ ResumeSize = 0;
+ }
+ NameSize = ( wcslen( Name) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + ResumeSize, Name, NameSize);
+ ResumeSize += NameSize;
+ (VOID)ResumeKeySet( pSession, ServerHandle, ResumeBuffer, ResumeSize, pResumeHandle);
+}
+
+
+BOOL RplFilterNext(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN OUT PRPL_FILTER pFilter,
+ OUT PBOOL pTableEnd
+ )
+{
+ JET_ERR JetError;
+
+ JetError = JetMove( pSession->SesId, TableId, JET_MoveNext, 0);
+ if ( JetError == JET_errSuccess) {
+ return( TRUE);
+ }
+ if ( pFilter == NULL) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_DB, (
+ "FilterNext: TableId=0x%x, JetError=%d", TableId, JetError));
+ *pTableEnd = TRUE;
+ return( TRUE); // assume end of table
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_DB, (
+ "FilterNext: TableId=0x%x, BootName=%ws, JetError=%d",
+ TableId, pFilter->BootName, JetError));
+ return( RplScan( pSession, TableId, TRUE, pFilter, pTableEnd));
+ }
+}
+
+
+BOOL RplFind(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PWCHAR Name
+ )
+{
+ JET_ERR JetError;
+ PCHAR Index;
+ JET_TABLEID TableId;
+
+ switch ( TableTag) {
+ case ADAPTER_TABLE_TAG:
+ TableId = pSession->AdapterTableId;
+ Index = ADAPTER_INDEX_AdapterName;
+ break;
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ Index = CONFIG_INDEX_ConfigName;
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ Index = PROFILE_INDEX_ProfileName;
+ break;
+ case VENDOR_TABLE_TAG:
+ TableId = pSession->VendorTableId;
+ Index = VENDOR_INDEX_VendorName;
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ Index = WKSTA_INDEX_WkstaName;
+ break;
+ default:
+ RPL_RETURN( FALSE);
+ }
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, Index));
+ CallB( JetMakeKey( pSession->SesId, TableId, Name, ( wcslen( Name) + 1) * sizeof(WCHAR), JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekEQ);
+ if ( JetError < 0) {
+#ifdef RPL_DEBUG
+ //
+ // JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ if ( JetError != JET_errRecordNotFound
+ && JetError != JET_errNoCurrentRecord) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ }
+#endif
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplFindByField(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PCHAR IndexName,
+ IN PWCHAR FieldName
+ )
+/*++
+ Returns TRUE if it can find a record (the first record) that has
+ FieldName as a value. Returns FALSE otherwise.
+
+ We cannot use SetIndexRange technique here because FieldName is only
+ the first part of an index & we would never find a match.
+--*/
+{
+ DWORD FieldNameSize;
+ JET_ERR JetError;
+ BYTE Data[ 300];
+ DWORD DataSize;
+ JET_TABLEID TableId;
+ JET_COLUMNID ColumnId;
+
+ switch ( TableTag) {
+ case CONFIG_TABLE_TAG:
+ TableId = pSession->ConfigTableId;
+ if ( strcmp( IndexName, CONFIG_INDEX_BootNameConfigName) == 0) {
+ ColumnId = ProfileTable[ CONFIG_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ case PROFILE_TABLE_TAG:
+ TableId = pSession->ProfileTableId;
+ if ( strcmp( IndexName, PROFILE_INDEX_ConfigNameProfileName) == 0) {
+ ColumnId = ProfileTable[ PROFILE_ConfigName].ColumnId;
+ } else if ( strcmp( IndexName, PROFILE_INDEX_BootNameProfileName) == 0) {
+ ColumnId = ProfileTable[ PROFILE_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ case WKSTA_TABLE_TAG:
+ TableId = pSession->WkstaTableId;
+ if ( strcmp( IndexName, WKSTA_INDEX_ProfileNameWkstaName) == 0) {
+ ColumnId = WkstaTable[ WKSTA_ProfileName].ColumnId;
+ } else if ( strcmp( IndexName, WKSTA_INDEX_BootNameWkstaName) == 0) {
+ ColumnId = WkstaTable[ WKSTA_BootName].ColumnId;
+ } else {
+ RPL_RETURN( FALSE);
+ }
+ break;
+ default:
+ RPL_RETURN( FALSE);
+ }
+
+ CallB( JetSetCurrentIndex( pSession->SesId, TableId, IndexName));
+ FieldNameSize = ( wcslen( FieldName) + 1) * sizeof(WCHAR);
+ CallB( JetMakeKey( pSession->SesId, TableId, FieldName, FieldNameSize, JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, TableId, JET_bitSeekGE);
+ if ( JetError < 0) {
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, TableId, ColumnId, Data,
+ sizeof( Data), &DataSize, 0, NULL));
+ if ( FieldNameSize != DataSize) {
+ return( FALSE);
+ }
+ if ( memcmp( FieldName, Data, DataSize) != 0) {
+ return( FALSE);
+ }
+ return( TRUE); // we found record using this field
+}
+
+
+DWORD AdapterNameToVendorId( IN PWCHAR AdapterName)
+{
+ WCHAR VendorName[ RPL_VENDOR_NAME_LENGTH + 1];
+ //
+ // Yes, we could zero the char in AdapterName, calculate VendorId,
+ // then restore char. But this is more robust though.
+ //
+ memcpy( VendorName, AdapterName, RPL_VENDOR_NAME_LENGTH*sizeof(WCHAR));
+ VendorName[ RPL_VENDOR_NAME_LENGTH] = 0;
+ return( wcstoul( VendorName, NULL, 16));
+}
diff --git a/private/net/svcdlls/rpl/server/dblib.h b/private/net/svcdlls/rpl/server/dblib.h
new file mode 100644
index 000000000..463a4dea3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/dblib.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dblib.h
+
+Abstract:
+
+ Exports from dblib.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+
+BOOL RplScan(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN BOOL FindNextBoot,
+ IN OUT PRPL_FILTER pFilter,
+ IN OUT PBOOL pTableEnd
+ );
+BOOL RplFilterFirst(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN OUT PRPL_FILTER pFilter,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ );
+BOOL RplFilterNext(
+ IN PRPL_SESSION pSession,
+ IN JET_TABLEID TableId,
+ IN OUT PRPL_FILTER pFilter,
+ OUT PBOOL pTableEnd
+ );
+VOID RplFilterSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PRPL_FILTER pFilter,
+ IN PWCHAR Name,
+ IN PDWORD pResumeHandle
+ );
+BOOL RplFind(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PWCHAR Name
+ );
+BOOL RplFindByField(
+ IN PRPL_SESSION pSession,
+ IN RPL_TABLE_TAG TableTag,
+ IN PCHAR IndexName,
+ IN PWCHAR FieldName
+ );
+DWORD AdapterNameToVendorId( IN PWCHAR AdapterName);
+
+
diff --git a/private/net/svcdlls/rpl/server/debug.c b/private/net/svcdlls/rpl/server/debug.c
new file mode 100644
index 000000000..e27c49bd7
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/debug.c
@@ -0,0 +1,141 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Debugging module. RpldDebugPrint() was adapted from TelnetDebugPrint()
+ in net\sockets\tcpcmd\telnet\debug.c.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "debug.h"
+
+#ifdef RPL_DEBUG
+
+#define RPL_PROMPT "[Rpl] "
+#define DEBUG_RECORD_SIZE 0x80
+#define DEBUG_BUFFER_SIZE 0x20000 // 2*64k, must be a power of 2
+#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1)
+
+// int RG_DebugLevel = -1; // max debugging
+// int RG_DebugLevel = 0; // no debugging, for public use
+
+int RG_DebugLevel; // needed by other modules
+int RG_Assert; // needed by other modules
+
+PCHAR RG_DebugBuffer;
+CRITICAL_SECTION RG_ProtectDebug;
+DWORD RG_DebugOffset;
+//
+// This buffer is used even when RG_DebugBuffer is not NULL.
+//
+char RG_DebugPublicBuffer[ 120];
+
+
+VOID RplDebugDelete( VOID)
+{
+ if ( RG_DebugBuffer != NULL) {
+ (VOID)GlobalFree( RG_DebugBuffer);
+ }
+ DeleteCriticalSection( &RG_ProtectDebug);
+}
+
+#define RPL_DEFAULT_DEBUG_LEVEL ( RPL_DEBUG_FLOW | \
+ RPL_DEBUG_SERVER | \
+ RPL_DEBUG_MAJOR )
+
+#define RPL_MAXIMUM_DEBUG_LEVEL (-1L)
+
+
+
+VOID RplDebugInitialize( VOID)
+{
+ //
+ // Set RG_Assert to 0. Without this the very first use of
+ // RplDump() will lead to an assert.
+ //
+ RG_Assert = 0;
+
+// RG_DebugLevel = RPL_DEFAULT_DEBUG_LEVEL;
+ RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL & ~RPL_DEBUG_MEMALLOC
+ & ~RPL_DEBUG_FLOWINIT & ~RPL_DEBUG_MISC;
+// RG_DebugLevel = RPL_MAXIMUM_DEBUG_LEVEL;
+// RG_DebugLevel = RPL_DEBUG_MISC;
+
+
+ RG_DebugBuffer = (PCHAR)GlobalAlloc(
+ GMEM_FIXED,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE
+ );
+ if ( RG_DebugBuffer != NULL) {
+ RtlFillMemory(
+ RG_DebugBuffer,
+ DEBUG_BUFFER_SIZE + DEBUG_RECORD_SIZE,
+ '*'
+ );
+ RG_DebugOffset = 0;
+ }
+ memcpy( RG_DebugPublicBuffer, RPL_PROMPT, sizeof( RPL_PROMPT) -1);
+
+ InitializeCriticalSection( &RG_ProtectDebug);
+}
+
+
+VOID _CRTAPI1 RplDebugPrint( CONST CHAR * format, ...)
+{
+ va_list arglist;
+
+ va_start( arglist, format);
+
+ EnterCriticalSection( &RG_ProtectDebug);
+
+ if ( RG_Assert == 0 && RG_DebugBuffer != NULL) {
+ RtlZeroMemory( RG_DebugBuffer + RG_DebugOffset, DEBUG_RECORD_SIZE);
+ vsprintf( RG_DebugBuffer + RG_DebugOffset, format, arglist);
+ RG_DebugOffset += DEBUG_RECORD_SIZE;
+ RG_DebugOffset &= DEBUG_BUFFER_MASK;
+ } else {
+ vsprintf( RG_DebugPublicBuffer + sizeof( RPL_PROMPT) - 1, format, arglist);
+ DbgPrint( "%s\n", RG_DebugPublicBuffer); // for kernel debugger
+ printf( "%s\n", RG_DebugPublicBuffer); // for user debugger
+ }
+
+ if ( RG_Assert != 0) {
+ ASSERT( FALSE); // break for checked build
+ DbgPrint( "[RplSvc] Running checked version of service on a free build.\n");
+ printf( "[RplSvc] Running checked version of service on a free build.\n");
+ }
+ while ( RG_Assert != 0) {
+ DbgPrint( "[RplSvc] Debug, then reset RG_Assert to 0.\n");
+ printf( "[RplSvc] Debug, then reset RG_Assert to 0.\n");
+ DbgUserBreakPoint();
+ Sleep( RG_Assert * 30000); // sleep for 30 seconds
+ }
+
+ LeaveCriticalSection( &RG_ProtectDebug);
+
+ va_end( arglist);
+
+} // RplDebugPrint
+
+
+#endif // RPL_DEBUG
+
+
+
diff --git a/private/net/svcdlls/rpl/server/debug.h b/private/net/svcdlls/rpl/server/debug.h
new file mode 100644
index 000000000..31706c1fe
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/debug.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+ Exports from debug.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPL_DEBUG
+VOID RplDebugInitialize( VOID);
+VOID RplDebugDelete( VOID);
+#endif // RPL_DEBUG
diff --git a/private/net/svcdlls/rpl/server/disk.c b/private/net/svcdlls/rpl/server/disk.c
new file mode 100644
index 000000000..1c53f8c52
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/disk.c
@@ -0,0 +1,318 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ Routines changing disk files (other than the database).
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "db.h"
+
+
+DWORD WkstaDiskSet(
+ IN DWORD Action,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing,
+ IN PWCHAR TargetWkstaName,
+ IN PWCHAR TargetProfileName,
+ IN DWORD TargetSharing
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ if ( TargetWkstaName != NULL) {
+ if ( TargetSharing = 0) {
+ TargetSharing = Sharing;
+ }
+ if ( TargetProfileName == NULL) {
+ TargetProfileName = ProfileName;
+ }
+ Error = WkstaDiskAdd( Action == ADD_NEW_BRANCHES,
+ Action == DEL_OLD_BRANCHES ? WkstaName : TargetWkstaName,
+ TargetProfileName, TargetSharing);
+ if ( Action != ADD_NEW_BRANCHES) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\DATA", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\MACHINES\\%ws\\DATA", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\LOGS", RG_DiskRplfiles, TargetWkstaName);
+ swprintf( Source, L"%ws\\MACHINES\\%ws\\LOGS", RG_DiskRplfiles, WkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else if ( TargetProfileName != NULL) {
+ DWORD Length;
+ if ( TargetSharing == 0) {
+ TargetSharing = Sharing;
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\%ws", RG_DiskRplfiles,
+ WkstaName,
+ Action == DEL_OLD_BRANCHES ? ProfileName : TargetProfileName);
+ Error = RplTreeDelete( Target);
+ if ( Action != ADD_NEW_BRANCHES) {
+ return( Error);
+ }
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Length = wcslen( Target);
+ wcscat( Target + Length, L"\\WKSTA");
+ swprintf( Source, L"%ws\\PROFILES\\%ws\\WKSTA.PRO", RG_DiskRplfiles, TargetProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( TargetSharing == WKSTA_FLAGS_SHARING_TRUE) {
+ return( Error);
+ }
+ swprintf( Target + Length, L"\\PRO");
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else if ( TargetSharing != 0) {
+ BOOL DeletePro;
+ BOOL CreatePro; // FALSE whenever DeletePro is FALSE
+ switch( Action) {
+ case ADD_NEW_BRANCHES:
+ DeletePro = TRUE;
+ CreatePro = (TargetSharing == WKSTA_FLAGS_SHARING_FALSE);
+ break;
+ case DEL_NEW_BRANCHES:
+ DeletePro = (TargetSharing == WKSTA_FLAGS_SHARING_FALSE);
+ CreatePro = FALSE;
+ break;
+ case DEL_OLD_BRANCHES:
+ DeletePro = (Sharing ==WKSTA_FLAGS_SHARING_FALSE);
+ CreatePro = FALSE;
+ }
+ if ( DeletePro == FALSE) {
+ RPL_ASSERT( CreatePro == FALSE);
+ return( NO_ERROR);
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws\\%ws\\PRO", RG_DiskRplfiles,
+ WkstaName, ProfileName);
+ Error = RplTreeDelete( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( CreatePro == FALSE) {
+ return( NO_ERROR);
+ }
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR WkstaName,
+ IN PWCHAR ProfileName,
+ IN DWORD Sharing
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, WkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ swprintf( Target, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, WkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ DWORD Length = wcslen( Target);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ //
+ // Note that here we have "MACHINE" - unlike "MACHINES" above. What a fun!
+ //
+ swprintf( Source, L"%ws\\CONFIGS\\MACHINE", RG_DiskRplfiles);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ //
+ // Currently RplTreeCopy fails on missing links. Therefore,
+ // we must first create directory below before RplTreeCopy
+ // can be called.
+ //
+ swprintf( Target + Length, L"\\%ws", ProfileName);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ wcscat( Target + Length, L"\\WKSTA");
+ swprintf( Source, L"%ws\\PROFILES\\%ws\\WKSTA.PRO", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( Sharing == WKSTA_FLAGS_SHARING_TRUE) {
+ return( Error);
+ }
+ swprintf( Target + Length, L"\\%ws\\PRO", ProfileName);
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceWkstaName,
+ IN PWCHAR TargetWkstaName
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ //
+ // TMPFILES tree is never cloned
+ //
+ swprintf( Target, L"%ws\\TMPFILES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+
+ //
+ // MACHINES tree is cloned except for the DATA subtree
+ //
+ swprintf( Target, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, TargetWkstaName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ swprintf( Source, L"%ws\\MACHINES\\%ws", RG_DiskRplfiles, SourceWkstaName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ wcscat( Target, L"\\DATA");
+ RplTreeDelete( Target);
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD ProfileDiskAdd(
+ IN BOOL Doit,
+ IN PWCHAR ProfileName,
+ IN PWCHAR DirName,
+ IN PWCHAR DirName2,
+ IN PWCHAR DirName3,
+ IN PWCHAR DirName4
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, ProfileName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName2 == NULL || *DirName2 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName2);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName3 == NULL || *DirName3 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName3);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR || DirName4 == NULL || *DirName4 == 0) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\CONFIGS\\%ws", RG_DiskRplfiles, DirName4);
+ Error = RplTreeCopy( Source, Target);
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+
+DWORD ProfileDiskClone(
+ IN BOOL Doit,
+ IN PWCHAR SourceProfileName,
+ IN PWCHAR TargetProfileName
+ )
+{
+ WCHAR Target[ MAX_PATH + 1];
+ WCHAR Source[ MAX_PATH + 1];
+ DWORD Error;
+
+ swprintf( Target, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, TargetProfileName);
+ RplTreeDelete( Target);
+ if ( Doit) {
+ Error = RplMakeDir( Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ swprintf( Source, L"%ws\\PROFILES\\%ws", RG_DiskRplfiles, SourceProfileName);
+ Error = RplTreeCopy( Source, Target);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ }
+ return( NO_ERROR);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/server/fitfile.c b/private/net/svcdlls/rpl/server/fitfile.c
new file mode 100644
index 000000000..9aa421920
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/fitfile.c
@@ -0,0 +1,442 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ fitfile.c
+
+Abstract:
+
+ Processes the FIT file referenced in workstation record, eliminating
+ the following keywords:
+
+ (DOSFIT) defines the UNC path of the temporary files used
+ by the new DOS boot (this will probably be removed very soon).
+
+ (CNAME) the netbios cname of the workstation
+
+ (PROFILE) a directory tree for the the shareable config files
+
+ (RPLFILES) share for the configuration and per workstation files
+
+ (SWAPPATH) share for the swap and tmp files, (== (RPLFILES) by default)
+
+ (RPLBINS) share for all executable files, (== (RPLFILES) by default)
+
+ (SRVNAME) the name of RPL server, by default the current netbios name.
+ The server name is usually replaced by the share names.
+
+ The keywords are replaced by the actual parameters. CNAME and PROFILES
+ are defined in the workstation line of RPL.map and the last parameters
+ are optional lanman.ini parameters. (RPLFILES) is by default
+ \\CurrentServer\RPLFILE and (SWAPPATH) and (RPLBINS) are the same share,
+ if they have not been defined in lanman.ini.
+ Procedure supports the tilde replacement (e.g: ~~~~~~~~~~2) similar to
+ the old IBM RIPL. OEMs may define their own tilde fileds in the
+ rpl.map workstation lines, if they really want to. The default system
+ uses the tilde replacement only for per workstation (PROFILE) and (CNAME).
+ The alias keys are first translated to tildes and then they are
+ translated to wksta line fields.
+
+ Also removes all extra comments and spaces from the fit to save DOS and
+ OS/2 memory.
+
+ Provides fitfile functionality similar to that contained in rmapopen.c
+ of LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "fitfile.h"
+
+#define TAB_CHAR L'\t'
+#define LEFT_BRACKET_CHAR L'('
+#define BACK_SLASH_CHAR L'\\'
+
+
+INT RplEatSpaces( IN OUT LPWSTR Source)
+/*++
+
+Routine Description:
+
+ Deletes the extra white space characters in the fit string.
+ Only one white space or tabulator is necessary.
+
+Arguments:
+
+ Source - string to process
+
+Return Value:
+
+ Length in characters of the modified string (not counting the
+ terminating NULL character).
+
+--*/
+{
+ LPWSTR Origin = Source;
+ LPWSTR Target = Source;
+ WCHAR ch;
+
+ for (;;) {
+
+ while ( (ch = *Target++ = *Source++)!=0 && ch!=SPACE_CHAR
+ && ch!=TAB_CHAR) {
+ NOTHING; // copy characters until nul, tab or space character is found
+ }
+
+ if ( ch == 0) {
+ break; // end of string
+ }
+
+ if (ch == TAB_CHAR) {
+ *(Target - 1) = SPACE_CHAR; // replace tab with a space
+ }
+
+ while (*Source == SPACE_CHAR || *Source == TAB_CHAR) {
+ Source++; // skip extra tabs & spaces
+ }
+
+ //
+ // If the next character is a space character (e.g. newline)
+ // then this space is unnecessary, write on it in the next time.
+ //
+ if ( iswspace(*Source)) {
+ Target--;
+ }
+ }
+ return( Target-Origin - 1); // no casts are needed
+
+} // EatSpaces()
+
+
+VOID RplFitAliasInit( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+ Must make sure number of these entries equals FIT_ALIAS_ARRAY_LENGTH
+--*/
+{
+ DWORD Index;
+ PFIT_ALIAS pFitAlias;
+ PFIT_ALIAS FitAliasArray;
+
+ pFitAlias = FitAliasArray = &( pWorkerData->FitAliasArray[0]);
+
+ pFitAlias->Key = L"(PROFILE)"; // profile name of RPL wksta
+ pFitAlias->Value = pWorkerData->ProfileName;
+
+ (++pFitAlias)->Key = L"(CNAME)"; // computer name of RPL wksta
+ pFitAlias->Value = pWorkerData->WkstaName;
+
+//
+// (RPL_SERVER_NAME) is not documented by us nor used in our FIT files.
+// We should probably remove this entry.
+//
+ (++pFitAlias)->Key = L"(RPL_SERVER_NAME)"; // computer name of RPL server
+ pFitAlias->Value = RG_ComputerName;
+
+//
+// (RPLFILES) must be the FIRST SHARE in the array, because it is the
+// default used in (TMPFILES) and (BINFILES) if they are not defined!
+//
+ (++pFitAlias)->Key = L"(RPLFILES)";
+ pFitAlias->Value = RG_UncRplfiles;
+
+ (++pFitAlias)->Key = L"(TMPFILES)";
+ pFitAlias->Value = L"TMPFILES";
+
+ (++pFitAlias)->Key = L"(BINFILES)";
+ pFitAlias->Value = L"BINFILES";
+
+//
+// (COMPUTER_NAME) is not documented by us nor used in our FIT files.
+// It has the same purpose as (CNAME) and was kept for compatibility
+// with some old NOKIA FIT files. We should probably remove this entry.
+//
+ (++pFitAlias)->Key = L"(COMPUTER_NAME)";
+ pFitAlias->Value = RG_ComputerName;
+
+ RPL_ASSERT( FitAliasArray + FIT_ALIAS_ARRAY_LENGTH == ++pFitAlias);
+
+ for ( Index = 0; Index < FIT_ALIAS_ARRAY_LENGTH; Index++) {
+ FitAliasArray[ Index].KeyLength = wcslen( FitAliasArray[ Index].Key);
+ FitAliasArray[ Index].ValueLength = wcslen( FitAliasArray[ Index].Value);
+ }
+}
+
+
+PFIT_ALIAS RplFitFind( IN PFIT_ALIAS FitAliasArray, IN LPWSTR Key)
+/*++
+
+Routine Description:
+ Looks for FIT_ALIAS containing input key string.
+
+Arguments:
+ Key - ptr to key string
+
+Return Value:
+ Returns the pointer of a FIT alias structure or NULL if the the key was not found.
+
+--*/
+{
+ DWORD index;
+
+ for ( index = 0; index < FIT_ALIAS_ARRAY_LENGTH; index++) {
+ if ( !wcsncmp( FitAliasArray[ index].Key, Key, FitAliasArray[ index].KeyLength)) {
+ return &(FitAliasArray[ index]);
+ }
+ }
+ return( NULL);
+}
+
+
+DWORD RplFitNewLength( IN LPWSTR pFitImage, IN PFIT_ALIAS FitAliasArray)
+{
+ PWCHAR Cursor;
+ PFIT_ALIAS pFitAlias;
+ INT Length;
+
+ //
+ // Calculate the length of the new fit file. Some key replacements
+ // will increase the total length, the other decrease it.
+ //
+ for ( Cursor = pFitImage, Length = wcslen( pFitImage);
+ ( Cursor = wcschr( Cursor, LEFT_BRACKET_CHAR)) != NULL;
+ Cursor++) {
+
+ if ( (pFitAlias = RplFitFind( FitAliasArray, Cursor)) != NULL) {
+ //
+ // We found a keyword, replacement should be made, see how this
+ // would change the total length of FIT data.
+ //
+ Length += (INT)pFitAlias->ValueLength - (INT)pFitAlias->KeyLength;
+ }
+ }
+ return( Length);
+}
+
+
+VOID RplFitReplaceKeys(
+ OUT PWCHAR Target,
+ IN PWCHAR Source,
+ IN PFIT_ALIAS FitAliasArray
+ )
+/*++
+ Replace keywords.
+
+--*/
+{
+ PFIT_ALIAS pFitAlias;
+ PWCHAR Cursor;
+ INT Length;
+
+ Cursor = Source; // Initailize
+
+ while ( (Cursor = wcschr( Cursor, LEFT_BRACKET_CHAR)) != NULL) {
+
+ if ( (pFitAlias = RplFitFind( FitAliasArray, Cursor)) != NULL) {
+ //
+ // Copy stuff before the keyword, then copy the replacement string.
+ //
+ Length = Cursor - Source; // no casts are needed
+ memcpy( Target, Source, Length * sizeof( WCHAR));
+ memcpy( Target + Length, pFitAlias->Value, pFitAlias->ValueLength * sizeof( WCHAR));
+ Source += Length + pFitAlias->KeyLength;
+ Target += Length + pFitAlias->ValueLength;
+ Cursor += pFitAlias->KeyLength; // skip entire key
+ } else {
+ Cursor++; // skip LEFT_BRACKET_CHAR
+ }
+ }
+ //
+ // Here LEFT_BRACKET is NULL and we still need to copy from Source to the end of the
+ // string, including the terminal zero.
+ //
+ wcscpy( Target, Source);
+}
+
+
+VOID RplStripComments( IN OUT LPWSTR buffer)
+/*++
+
+Routine Description:
+ Skips leading spaces in each line and removes all comments lines
+ from a buffer containing FIT file.
+
+Arguments:
+ buffer - pointer to buffer containing FIT file, the buffer is NULL
+ terminated.
+
+Return Value:
+ None.
+
+--*/
+{
+ PWCHAR Cursor; // running cursor
+ PWCHAR LineEnd; // pointer to NEW_LINE_CHAR
+
+ Cursor = buffer;
+ for ( ; ;) {
+ //
+ // We are at the beginning of a line, skip leading white space characters.
+ //
+ Cursor += wcsspn( Cursor, L" \f\n\r\t\v");
+ if ( *Cursor == 0) {
+ break; // we reached the end of a string
+ }
+
+ LineEnd = wcschr( Cursor, NEW_LINE_CHAR);
+ if ( LineEnd == NULL) {
+ break; // ignore stuff without end of line
+ }
+ LineEnd++; // make it point beyond NEW_LINE_CHAR we just found
+
+ //
+ // Copy only the non comment lines. Memmove below works even
+ // for overlapping memory regions.
+ //
+ if ( *Cursor != COMMENT_CHAR) {
+ INT Length = LineEnd - Cursor;
+ buffer = (PWCHAR)memmove( buffer, Cursor, Length * sizeof(WCHAR)) + Length;
+ }
+ Cursor = LineEnd;
+ }
+ *buffer = 0; // null terminate
+}
+
+
+
+VOID RplFitStripRplfiles( IN OUT LPWSTR Buffer)
+/*++
+ The first line in a FIT file is a special case, it must contain the
+ default UNC share used in a FIT file. This UNC name need not be
+ anywhere else in a FIT file, so remove all other references.
+
+ BUGBUG: should we insist FIT file contains UNC name in the first line ??
+
+--*/
+{
+ PWCHAR Cursor;
+ INT Length;
+
+ if ( Buffer[0] == BACK_SLASH_CHAR && Buffer[1] == BACK_SLASH_CHAR) {
+ //
+ // First line contains UNC name, remember its length.
+ //
+ Cursor = wcspbrk( Buffer, L" \f\n\r\t\v");
+ Length = Cursor - Buffer; // no casts are needed
+
+ while ( (Cursor = wcsstr( Cursor, DOUBLE_BACK_SLASH_STRING)) != NULL) {
+ if ( !wcsncmp( Buffer, Cursor, Length) && Cursor[ Length] == BACK_SLASH_CHAR) {
+ memset( Cursor, SPACE_CHAR, (Length + 1) * sizeof( WCHAR));
+ Cursor += Length + 1;
+ } else {
+ Cursor += (sizeof(DOUBLE_BACK_SLASH_STRING)/sizeof(WCHAR) - 1);
+ }
+ }
+ }
+}
+
+
+BOOL RplFitFile( IN OUT PRPL_WORKER_DATA pWorkerData)
+{
+ PFIT_ALIAS FitAliasArray;
+ LPWSTR UnicodeString; // UNICODE content of FIT file
+ PWCHAR Target;
+ INT UnicodeStringLength;
+ BOOL Success;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++FitFile"));
+
+ Success = FALSE;
+ FitAliasArray = &( pWorkerData->FitAliasArray[ 0]);
+
+ //
+ // Initalize FIT_ALIAS[] array since it will be used in calculations below.
+ //
+ RplFitAliasInit( pWorkerData);
+
+ //
+ // Read FIT file data as a UNICODE string.
+ //
+ UnicodeString = RplReadTextFile( pWorkerData->MemoryHandle,
+ pWorkerData->FitFile, MAX_FIT_FILE_SIZE);
+ if ( UnicodeString == NULL) {
+ RplDump( ++RG_Assert, ( "FitFile=%ws", pWorkerData->FitFile));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->FitFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ //
+ // Remove the comments and all unnecessary spaces. There should be not
+ // extra characters in FIT, because they eat both DOS and OS/2 memory.
+ //
+ RplStripComments( UnicodeString);
+
+ //
+ // Calculate space needed for a UNICODE version of FIT file after we make
+ // all key replacements.
+ //
+ UnicodeStringLength = RplFitNewLength( UnicodeString, FitAliasArray);
+
+ //
+ // Allocate space for UNICODE version of FIT file.
+ //
+ Target = RplMemAlloc( pWorkerData->MemoryHandle, (UnicodeStringLength+1) * sizeof( WCHAR));
+ if ( Target == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ goto cleanup;
+ }
+
+ RplFitReplaceKeys( Target, UnicodeString, FitAliasArray);
+ RplMemFree( pWorkerData->MemoryHandle, UnicodeString);
+ UnicodeString = Target;
+
+ //
+ // Get rid of RPLFILES duplicates.
+ //
+ RplFitStripRplfiles( UnicodeString);
+
+ UnicodeStringLength = RplEatSpaces( UnicodeString); // compactify
+ _wcsupr( UnicodeString); // upppercase
+
+ //
+ // Convert UNICODE fit string into DBCS fit string.
+ //
+ pWorkerData->ClientFitSize = RplUnicodeToDbcs(
+ pWorkerData->MemoryHandle,
+ UnicodeString,
+ UnicodeStringLength,
+ MAX_FIT_FILE_SIZE,
+ &(pWorkerData->ClientFit)
+ );
+ if ( pWorkerData->ClientFitSize == 0) {
+ RplDump( ++RG_Assert, ( "UnicodeString=0x%x, UnicodeString=%ws",
+ UnicodeString, UnicodeString));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->FitFile;
+ pWorkerData->EventId = NELOG_RplWkstaFileSize;
+ goto cleanup;
+ }
+
+ Success = TRUE;
+
+cleanup:
+ if ( UnicodeString != NULL) {
+ RplMemFree( pWorkerData->MemoryHandle, UnicodeString);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--FitFile"));
+ return( Success);
+}
diff --git a/private/net/svcdlls/rpl/server/fitfile.h b/private/net/svcdlls/rpl/server/fitfile.h
new file mode 100644
index 000000000..4ccb4fc01
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/fitfile.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fitfile.h
+
+Abstract:
+
+ Exports from fitfile.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplFitFile( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
diff --git a/private/net/svcdlls/rpl/server/library.c b/private/net/svcdlls/rpl/server/library.c
new file mode 100644
index 000000000..414c85fc2
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/library.c
@@ -0,0 +1,273 @@
+/*++
+
+Copyright (c) 1987 - 1993 Microsoft Corporation
+
+Module Name:
+
+ library.c
+
+ Provides similar functionality to rpllib.c in LANMAN 2.1 code.
+
+Abstract:
+
+ Common library routines.
+
+Author:
+
+ Vladimir Z. Vulovic 25 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+
+
+
+LPWSTR RplGetLastPathComponent( IN LPWSTR path_str)
+/*++
+
+Routine Description:
+ Returns the last component of the path.
+
+Arguments:
+ path_str - pointer to path string
+
+Return Value:
+ Pointer to the last component of the path.
+
+--*/
+{
+ LPWSTR ret_str;
+ WCHAR ch;
+
+ for( ch = *( ret_str = path_str); ch != 0; ch = *(++path_str)) {
+ if ( ch == '\\' || ch == '/') {
+ ret_str = path_str + 1; // remember most recent component
+ }
+ }
+ return( ret_str);
+}
+
+
+
+HANDLE RplFileOpen( IN LPWSTR FilePath)
+/*++
+
+Routine Description:
+
+ Open existing file for reading, at the same time denying write
+ access to others. Note that WIN32 errors are basically identical
+ to OS/2 errors!
+
+ In case of special errors we may try a number of times to open the
+ requested file.
+
+Arguments:
+
+ FilePath - of file to open
+
+Return Value:
+
+ A valid file handle if successful, else INVALID_HANDLE_VALUE if
+ unsuccessful.
+
+--*/
+{
+ DWORD status;
+ DWORD retry_count;
+ HANDLE handle;
+
+ //
+ // If file is being used by another process, we make up to 40 retries
+ // each one lasting 0.750 seconds (for a max total of 30 seconds) before
+ // we resign. Configuration programs may lock the files temporarily,
+ // but if somebody locks a file for a long time, then we must die.
+ //
+ for ( retry_count = 0; retry_count < MAX_OPEN_RETRY; retry_count++) {
+
+ handle = CreateFile( FilePath, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( handle != INVALID_HANDLE_VALUE) {
+ return( handle); // success, all done
+ }
+
+ status = GetLastError();
+
+ if ( status != ERROR_SHARING_VIOLATION) {
+ break; // bad, unexpected case
+ }
+
+ Sleep( 750L );
+ }
+ RplReportEvent( NELOG_Init_OpenCreate_Err, FilePath, sizeof(WORD), (PBYTE)&status);
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,( "FileOpen(%ws) failed, status = %d", FilePath, status));
+ return( INVALID_HANDLE_VALUE);
+}
+
+
+
+LPWSTR RplReadTextFile(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ )
+/*++
+
+Routine Description:
+
+ Reads text file, converts its content from dbcs to unicode, and returns
+ a pointer to newly allocated unicode buffer.
+
+Arguments:
+
+Return Value:
+
+ Pointer to unicode buffer table if successful, NULL otherwise.
+
+--*/
+{
+ PBYTE DbcsString = NULL;
+ DWORD DbcsSize;
+ PWCHAR UnicodeString;
+ DWORD UnicodeSize;
+ int UnicodeStringLength;
+ HANDLE FileHandle;
+ DWORD BytesRead;
+ BOOL success = FALSE;
+ PWCHAR pWchar;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++ReadTextFile:0x%x,%ws", MemoryHandle, FileName));
+
+ FileHandle = RplFileOpen( FileName);
+ if ( FileHandle == INVALID_HANDLE_VALUE) {
+ goto exit;
+ }
+ DbcsSize = GetFileSize( FileHandle, NULL); // does not include 0x1A at the file end
+ if ( DbcsSize == INVALID_FILE_SIZE || DbcsSize > MaxFileSize) {
+ goto exit;
+ }
+ DbcsString = RplMemAlloc( MemoryHandle, DbcsSize);
+ if ( DbcsString == NULL) {
+ goto exit;
+ }
+
+ UnicodeSize = ( DbcsSize + 1) * sizeof(WCHAR); // extra 1 for terminating NULL
+ UnicodeString = RplMemAlloc( MemoryHandle, UnicodeSize);
+ if ( UnicodeString == NULL) {
+ goto exit;
+ }
+
+ if ( !ReadFile( FileHandle, DbcsString, DbcsSize, &BytesRead, NULL)) {
+ goto exit;
+ }
+
+ if ( BytesRead != DbcsSize) {
+ goto exit;
+ }
+
+ UnicodeStringLength = MultiByteToWideChar(
+ CP_OEMCP, // text files stored in OEM
+ MB_PRECOMPOSED, DbcsString, DbcsSize, UnicodeString,
+ UnicodeSize);
+ if ( UnicodeStringLength == 0) {
+ goto exit;
+ }
+
+ //
+ // Null-terminate string! JonN 8/7/94
+ //
+ UnicodeString[ UnicodeStringLength] = 0;
+
+ //
+ // If file has END_OF_TEXT_FILE_CHAR, truncate the text there.
+ //
+ pWchar = wcschr( UnicodeString, END_OF_TEXT_FILE_CHAR);
+ if ( pWchar != NULL) {
+ *pWchar = 0;
+ }
+
+ success = TRUE;
+
+exit:
+
+ if ( FileHandle != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( FileHandle);
+ }
+ if ( DbcsString != NULL) {
+ RplMemFree( MemoryHandle, DbcsString);
+ }
+
+ if ( success != TRUE) {
+ RplMemFree( MemoryHandle, UnicodeString);
+ UnicodeString = NULL;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--ReadTextFile:0x%x,%ws", MemoryHandle, FileName));
+ return( UnicodeString);
+
+} // RplReadTextFile
+
+
+DWORD RplUnicodeToDbcs(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR UnicodeString,
+ IN INT UnicodeStringLength,
+ IN DWORD MaxDbcsStringSize,
+ OUT LPSTR * pDbcsString
+ )
+/*++
+ Allocates DBCS string corresponding to a UNICODE string.
+
+ Returns size of buffer needed for DbcsString, counting space needed for
+ the terminal null byte.
+--*/
+{
+ LPSTR DbcsString;
+ DWORD ByteCount;
+
+ if ( UnicodeStringLength == -1) {
+ UnicodeStringLength = wcslen( UnicodeString);
+ }
+
+ //
+ // Assumed the worst case where every UNICODE char maps to a double
+ // byte DBCS char (except for DBCS terminating null char).
+ //
+ ByteCount = UnicodeStringLength * sizeof(WCHAR) + sizeof(CHAR);
+
+ //
+ // If caller's limit is more stringent, use it.
+ //
+ if ( ByteCount > MaxDbcsStringSize) {
+ ByteCount = MaxDbcsStringSize;
+ }
+
+ DbcsString = RplMemAlloc( MemoryHandle, ByteCount);
+ if ( DbcsString == NULL) {
+ RPL_RETURN( 0);
+ }
+
+ ByteCount = WideCharToMultiByte( // not counting terminal null byte
+ CP_OEMCP, // always use OEM
+ 0,
+ UnicodeString,
+ UnicodeStringLength,
+ DbcsString, // dbcs string
+ ByteCount,
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ RplMemFree( MemoryHandle, DbcsString);
+ RPL_RETURN( 0);
+ }
+ DbcsString[ ByteCount] = 0; // this may be redundant
+ *pDbcsString = DbcsString;
+ return( ByteCount + 1); // caller expects terminal null byte to be counted
+}
+
diff --git a/private/net/svcdlls/rpl/server/local.h b/private/net/svcdlls/rpl/server/local.h
new file mode 100644
index 000000000..b21ddac9e
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/local.h
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ local.h
+
+Abstract:
+
+ Main include file for Remote initial Program Load service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include <nt.h> // ntexapi.h\NtQuerySystemTime
+#include <ntrtl.h> // RtlTimeToSecondsSince1970
+#include <nturtl.h>
+
+#include <windows.h> // DWORD, IN, File APIs, etc.
+#include <stdlib.h>
+#include <lmcons.h>
+
+#include <stdio.h> // vsprintf
+#include <ctype.h> // isspace
+
+#include <lmerr.h> // NERR_RplBootStartFailed - used to be here
+#include <lmapibuf.h> // NetApiBufferFree
+#include <netlib.h>
+
+#include <lmsvc.h>
+
+#include <lmalert.h> // STD_ALERT ALERT_OTHER_INFO
+
+#include <lmerrlog.h>
+#include <alertmsg.h>
+#include <lmserver.h>
+#include <netlib.h>
+#include <netlock.h> // Lock data types, functions, and macros.
+#include <thread.h> // FORMAT_NET_THREAD_ID, NetpCurrentThread().
+
+#include <lmshare.h> // PSHARE_INFO_2
+#include <lmaccess.h> // NetGroupGetInfo
+#include <lmconfig.h> // NetConfigGet
+#include <nb30.h> // NCB
+
+
+//
+// Global types and constants (used in all RPL server files).
+//
+
+#include <riplcons.h> // includes __JET500 flag
+#include <jet.h>
+#include <rpldebug.h>
+#include <rpldll.h> // rplnet.dll entry points & return codes
+#include <rpllib.h> // AddKey(), MapJetError(), FillTcpIpString()
+#include <ripltyps.h>
+
+#include <rpc.h> // DataTypes and runtime APIs
+#include <rpcutil.h> // Prototypes for MIDL user functions
+//
+// rplsvc_s.h includes imports.h which includes lmrpl.h. This allows us
+// to use API-related DS in the rest of the service code.
+//
+#include <rplsvc_s.h> // Generated by the MIDL complier
+
+
+//
+// Other defines
+//
+
+#define DOUBLE_BACK_SLASH_STRING L"\\\\"
+
+//
+// BITMASK to be used with Flags field in RPL_WKSTA data structure
+//
+#define RPL_WKSTA_PERSONAL 0x01
+
+#define MAX_ADAPTERS 12 // "rpl1" through "rpl12"
+
+
+#define MIN_BBLOCK_BASE 0xc00
+
+//
+// All BBC files are very small, typically around 1K.
+// Use 32K as the absolute maximum on BBC file size.
+//
+#define MAX_BBC_FILE_SIZE 0x8000 // for no compelling reason
+
+#define MAX_FIT_FILE_SIZE 0xF000 // some arbitrary value
+#define MAX_SIZEOF_DBCS_PATH (PATHLEN *sizeof(WCHAR)+sizeof(CHAR))
+
+#define SERVER_LINE_ID_LEN 12
+
+#define DEFAULT_RETRY_COUNT 3
+#define DEFAULT_RETRY_TIMEOUT 10
+#define DEFAULT_WINDOW_SIZE MAXWORD
+#define DEFAULT_SEND_FINAL_REQ FALSE
+
+#define MAX_XMIT_ERRORS 30 // max contiguous fatal xmit failures
+
+//
+// status bits of main_action_status
+//
+
+#define TERMINATE_SERVICE 1
+#define REINITIALIZE_SERVICE 2
+#define REINITIALIZATION_PENDING 4
+#define TERMINATION_PENDING 8
+
+#define ERROR_HANDLING (DWORD)(-1)
+#define NO_ERROR_HANDLING 0
+#define MAX_OPEN_RETRY 40
+#define MAX_OEM_NAME 7
+
+//
+// Constants of the actual ripl server
+//
+
+#define RPL_DLL_SKELETON "rpl"
+#define WORKER_THREAD_MIN 2
+#define MAX_FRAME_SIZE 0x1000 // exactly 1 page, or 4KB
+#define MAX_SUB_SEGMENT_SIZE 0xffe0
+#define SEND_BUF_SEG_SIZE 17000
+//
+// MAX_ADAPTER_INFO_SIZE is now defined to be the sum of upper size
+// estimates for DLC buffer (0xA000) and private adapter info (2000).
+//
+#define MAX_ADAPTER_INFO_SIZE (0xA000 + 2000)
+
+#define LAN_ADAPTER0 0
+#define CHECK_SUM_ERROR 9676
+#define SF_REQ_TIMEOUT 7 //IBM waits 3s for lost frame & (ACK lost)
+#define MAX_FILE_HANDLES_INC 20 // increment of max fhs
+#define UNINSTALL_TIME 100 // 10 seconds, actually: (1 - 150)
+
+#define RPL_KEYBOARD_DCP L"KEYBOARD.DCP"
+#define RIPL_CHECK_SUM L"rplchecksum"
+#define RPLDIR_DEF L"rpldir"
+#define RPL_DIR_1 L"rpl1"
+#define YES L"yes"
+#define RPL_LOGON_KBD L"rpllogonkbd"
+#define LANMAN_INI L"lanman.ini"
+#define RPL_REMARK L"Remoteboot service share"
+#define RPLUSER_GROUP L"RPLUSER"
+
+//
+// JETBUG entries are needed for workaround for JetMakeKey-JetSeek bug.
+// If this bug gets ever fixed, they should be removed (undef RPL_JETBUG)
+//
+#define RPL_JETBUG
+#ifdef RPL_JETBUG
+#define JETBUG_STRING L" "
+#define JETBUG_STRING_SIZE sizeof( JETBUG_STRING)
+#define JETBUG_STRING_LENGTH ((DWORD)(sizeof( JETBUG_STRING)/sizeof(WCHAR) - 1))
+#else
+#define JETBUG_STRING_LENGTH 0
+#endif
+
+//
+// TIMEOUTS
+//
+
+#define GET_SF_REQUEST_TIMEOUT 6000L // not error and quite common
+
+// implement this with fast retries
+#define LONG_WAIT_ACK_TIMEOUT 30000L // wksta sent no resend requests!
+#define WAIT_ACK_TIMEOUT 1000L // short wait for the lost frames
+
+#define MAX_ERR_COUNT 4
+#define MAX_RETRIES 5 // retry count before termination
+
+#define OFFSET_BUF_LEN 64 // space for the whole RPLBoot hdr
+
+/*++
+ RPL service must send NLS (DBCS ??) messages in a layout expected by RPL client
+ (from rplboot\rplboot.asm):
+
+ ;------------------------------------------------------------------------
+ ; NLS messages structure - used in RPLOADR and DLCBOOT
+ ;------------------------------------------------------------------------
+ nlsmsglen equ 80 ;max number of bytes per msg
+ nlsmsg struc
+ db nlsmsglen dup(' '); ;<space fill>
+ db 0dh ;<cr>
+ db 0ah ;<lr>
+ db '$' ;<terminator>
+ nlsmsg ends
+--*/
+
+#define DBCS_SINGLE_MESSAGE_BUFFER_SIZE 83
+
+//
+// Typedefs
+//
+
+typedef struct _FLIST_TBL {
+ //
+ // UNICODE name. Used for work on the server.
+ //
+ LPWSTR FileName;
+ //
+ // Same file name but without path and in DBCS. Sent to client.
+ //
+ LPSTR DbcsFileName;
+ DWORD DbcsFileNameSize;
+
+ //
+ //
+ // Size of memory needed for "param_list", not counting terminating null
+ // byte, is kept in FILE_DATA.param_len entry.
+ //
+ LPSTR param_list; // a DBCS string, UNICODE version not needed
+ LPWSTR path_name;
+ FILE_DATA FileData; // structure to be saved to dump_tbl
+} FLIST_TBL, *PFLIST_TBL;
+
+typedef struct _CONFIG_TYPE {
+ LPWSTR id;
+ DWORD type;
+} CONFIG_TYPE, *PCONFIG_TYPE;
+
+#define FIT_ALIAS_ARRAY_LENGTH 7 // poor initialization
+
+typedef struct _FIT_ALIAS {
+ LPWSTR Key; // keyword, or macro
+ LPWSTR Value; // replacement value for the keyword
+ DWORD KeyLength; // length of keyword, or macro
+ DWORD ValueLength; // length of replacement value for the keyword
+} FIT_ALIAS, *PFIT_ALIAS;
+
+
+//
+// parameters sent to worker thread
+//
+// thread_id should be initialized somewhere via GetCurrentThreadId()
+//
+
+struct _RPL_REQUEST_PARAMS;
+typedef struct _RPL_REQUEST_PARAMS RPL_REQUEST_PARAMS, *PRPL_REQUEST_PARAMS;
+
+struct _RPL_WORKER_PARAMS;
+typedef struct _RPL_WORKER_PARAMS RPL_WORKER_PARAMS, *PRPL_WORKER_PARAMS;
+
+typedef struct _RPL_REQUEST_PARAMS {
+ PRPL_REQUEST_PARAMS pRequestParams; // next in a queue of request threads
+ HANDLE ThreadHandle;
+ DWORD ThreadId;
+ PRPL_WORKER_PARAMS pWorkerParams; // first in a queue of worker threads
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRCB FreeRcbList; // list of free rcb blocks
+ PRCB BusyRcbList; // list of reserved RCBs
+ BOOL Exiting;
+} RPL_REQUEST_PARAMS , *PRPL_REQUEST_PARAMS;
+
+typedef struct _RPL_WORKER_PARAMS { // worker thread parameters
+ PRPL_WORKER_PARAMS pWorkerParams; // next in a queue of worker threads
+ HANDLE ThreadHandle;
+ DWORD ThreadId;
+ PRPL_REQUEST_PARAMS pRequestParams;
+ PRCB pRcb;
+ BOOL Exiting;
+} RPL_WORKER_PARAMS, *PRPL_WORKER_PARAMS;
+
+typedef struct _RPL_WORKER_DATA {
+ //
+ // These are addresses of on the stack variables from the Request thread.
+ //
+ PPRCB pFreeRcbList; // base of free rcb list
+ PPRCB pBusyRcbList; // base of reserved rcbs
+ //
+ // The following three pointers are allocated via RG_MemoryHandle.
+ // Everything else in this data structure, including the structure itself
+ // is allocated via WORKER_DATA.MemoryHandle == WORKER_PARAMS.MemoryHandle.
+ //
+ PRCB pRcb; // resource control block
+ PRPL_REQUEST_PARAMS pRequestParams; // ptr to request threads parm block
+ POPEN_INFO pOpenInfo; // open info of the cur RPL DLL
+ //
+ // The following are the entries read from database.
+ // WkstaName & TcpIp* are sent to the client in wksta line.
+ // WkstaName & ProfileName are used to make changes in fit file.
+ //
+ WCHAR WkstaName[ RPL_MAX_WKSTA_NAME_LENGTH + 1];
+ WCHAR ProfileName[ RPL_MAX_PROFILE_NAME_LENGTH + 1];
+ DWORD TcpIpAddress;
+ DWORD TcpIpSubnet;
+ DWORD TcpIpGateway;
+ WCHAR LogonInput;
+ CHAR DisableDhcp; // '1' ('0') if DHCP is disabled (enabled)
+ DWORD WindowSize;
+ BOOL FinalAck; // SendFinalRequest;
+
+ HANDLE MemoryHandle; // for worker thread
+ LPBYTE WkstaLine; // old style, RPL.MAP line
+
+ //
+ // Fields describing the boot block configuration file.
+ //
+ LPWSTR BbcFile; // full path to BBC file
+ PFLIST_TBL flist_tbl; // files sent to RPLBOOT
+ BOOL MakePatch; // TRUE if patch to RPLBOOT.SYS is needed
+ DWORD PatchOffset; // relevant if MakePatch is TRUE
+ PRESOURCE_TBL resource_tbl; // file list given to the loader
+ PBYTE id_str; // id string of the server
+ DWORD min_wksta_buf; // min size of wksta specific data
+ DWORD file_block_base; // relative addr of file block in boot block
+ DWORD boot_block_base; // base phys addr of boot block
+ DWORD flist_tbl_len;
+ DWORD loader_i; // index of OS/2 loader in flist_tbl
+ DWORD rplboot_i; // index of rplboot.sys in file list
+ DWORD resource_tbl_size; // size of the table (in bytes)
+ LPWSTR LineBuffer; // used for parsing a line into words
+ DWORD LineBufferSize; // size of LineBuffer
+ LPWSTR * WordTable; // used for parsing a line into words
+
+ //
+ // Fields describing the file index table file.
+ //
+ // In order to get full path to fit file we need to check wksta flags
+ // to find out profile type (personal or shared) then read the
+ // corresponding file path from profile record.
+ //
+ LPWSTR FitFile; // full path to FIT file
+ LPSTR ClientFit; // string with DBCS content of FIT file sent to client
+ DWORD ClientFitSize; // size of above string
+ FIT_ALIAS FitAliasArray[ FIT_ALIAS_ARRAY_LENGTH];
+
+
+ //
+ // Fields initalized (wksta_buf also created) in RplMakeWkstaBuf()
+ //
+ //
+ // wksta_buf contains BOOT_DATA, old RPL.MAP workstation line, processed FIT file,
+ // BOOT_BLOCK_HDR and multiboot array.
+ //
+ // It does not contain files mentioned in boot block configuration file.
+ //
+ PBYTE wksta_buf; // ptr to wksta specific data
+
+ //
+ // size of wksta_buf
+ //
+ DWORD wksta_buf_len; // size in bytes of wksta specific data
+
+ //
+ //
+ //
+ DWORD base_address; // start address of buffer, first send
+ DWORD jump_address; // start address of program, last send
+ DWORD fblock_base_offset; // base offset (from 0) of file block
+
+ DWORD send_buf_len; // length of network send buffer
+
+ //
+ // The following group of fields is used while reading data from
+ // files mentioned in the boot block configuration file. These
+ // fields are initialized in RplOpenData()
+ //
+
+ //
+ // "pDataBuffer" is used to read data from disk files, then to send this
+ // data to a remote client.
+ //
+ PBYTE pDataBuffer; // read data here, then send it out
+ DWORD cur_flist_i; // current index for FLIST_TBL array
+ DWORD cur_offset; // current offset of boot block
+ DWORD cur_file_base_offset; // base offset of the current file
+ BOOL is_end_of_bblock; // set if end of boot block
+ HANDLE hFile; // current file handle
+ //
+ // ChecksumBuffer - buffer used to checksum files, when checksums are needed.
+ //
+ PBYTE ChecksumBuffer; // used to checksum files when
+ DWORD EventId;
+ PWCHAR EventStrings[ 5]; // null terminated
+} RPL_WORKER_DATA, *PRPL_WORKER_DATA;
+
+
+#define NOTIFY_WKSTA_RECORD 0 // disabled wksta record for main thread to write to RPL.map
+
+typedef struct _ADMIN_ALERT_DATA{
+ DWORD alrtad_errcode;
+ DWORD alrtad_numstrings;
+} ADMIN_ALERT_DATA;
+
+typedef struct _ADAPTER_STATUS_BUFFER {
+ DWORD id_low;
+ DWORD id_mid;
+ DWORD id_high;
+ BYTE jumper_status;
+ BYTE self_test;
+ DWORD sw_version;
+ BYTE error_statistics[48];
+ DWORD name_tbl_len;
+ BYTE name1[18];
+ BYTE name2[18];
+ BYTE name3[18];
+ BYTE name4[18];
+ BYTE name5[18];
+ BYTE name6[18];
+ BYTE name7[18];
+ BYTE name8[18];
+ BYTE name9[18];
+ BYTE name10[18];
+ BYTE name11[18];
+ BYTE name12[18];
+ BYTE name13[18];
+ BYTE name14[18];
+ BYTE name15[18];
+ BYTE name16[18];
+} ADAPTER_STATUS_BUFFER;
+
+typedef enum {IMMEDIATE_ERROR, WAIT_RESOURCES} BLOCKTYPE;
+
+#ifdef UNICODE
+
+#define RPL_TO_UNICODE( arg) arg
+#define RPL_FREE_UNICODE( arg)
+
+#else // UNICODE
+
+WCHAR * RplAsciizToUnicodez( IN CHAR * Asciiz);
+VOID RplFreeUnicodez( IN WCHAR * Unicodez);
+#define RPL_TO_UNICODE( arg) RplAsciizToUnicodez( arg)
+#define RPL_FREE_UNICODE( arg) RplFreeUnicodez( arg)
+
+#endif // UNICODE
+
+
+#define ALERT_LONG_WAIT 10000L // used to be in alert.h
+
+//
+// Bitmask used with RG_Tasks. They describe outstanding tasks to be
+// done by the main rpl service thread once it wakes up.
+//
+
+#define RPL_SERVICE_SHUTDOWN 0x0004
+
+
+#define RPL_WAIT_HINT_TIME 15000L // 15 seconds for now
+
+
+
+
+#define TMPFILES_IDX 4
+#define BINFILES_IDX 5
+
+
+//
+// termination addresses of all rpl DLLs have been saved to this list
+//
+
+typedef DWORD ( * TERMINATION_ADDRESS)( POPEN_INFO);
+
+typedef struct _TERM_LIST {
+ struct _TERM_LIST * next;
+ POPEN_INFO pOpenInfo;
+} TERM_LIST, *PTERM_LIST;
+
+
+#define INVALID_ERROR_PARAMETER ((DWORD)(-1)) // internal usage in api code
+
+typedef struct _RPL_SESSION {
+ JET_SESID SesId; // jet session identifier
+ JET_DBID DbId; // jet database identifier
+ JET_TABLEID AdapterTableId;
+ JET_TABLEID BootTableId;
+ JET_TABLEID ConfigTableId;
+ JET_TABLEID ProfileTableId;
+ JET_TABLEID ResumeTableId;
+ JET_TABLEID VendorTableId;
+ JET_TABLEID WkstaTableId;
+} RPL_SESSION, *PRPL_SESSION;
+
+
+//
+// Function Prototypes
+//
+
+//
+// memory.c
+//
+
+DWORD RplMemInit( PHANDLE pMemoryHandle);
+
+PVOID RplMemAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD size
+ );
+
+VOID RplMemFree(
+ IN HANDLE MemoryHandle,
+ IN PVOID mem_ptr
+ );
+
+VOID RplMemClose( IN HANDLE MemoryHandle);
+
+PVOID RplMemReAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD Flags,
+ IN PVOID old_ptr,
+ IN DWORD new_size
+ );
+
+
+//
+// library.c
+//
+
+LPWSTR RplGetLastPathComponent( IN LPWSTR FilePath);
+HANDLE RplFileOpen( IN LPWSTR FilePath);
+
+LPWSTR RplReadTextFile(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR FileName,
+ IN DWORD MaxFileSize
+ );
+DWORD RplUnicodeToDbcs(
+ IN HANDLE MemoryHandle,
+ IN LPWSTR UnicodeString,
+ IN INT UnicodeStringLength,
+ IN DWORD MaxDbcsStringSize,
+ OUT LPSTR * pDbcsString
+ );
+
+//
+// rplmain.c
+//
+
+VOID RPL_main(
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ );
+VOID RplInitError(
+ DWORD ErrorCode,
+ PCHAR ErrorIdString
+ );
+BOOL RplServiceAttemptStop( VOID);
+DWORD RplUpdateStatus( VOID);
+
+
+//
+// Declare/Define all global variables.
+//
+
+#include "rpldata.h" // defines/declares global data structures
diff --git a/private/net/svcdlls/rpl/server/makefile b/private/net/svcdlls/rpl/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/rpl/server/memory.c b/private/net/svcdlls/rpl/server/memory.c
new file mode 100644
index 000000000..15b9dc34a
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/memory.c
@@ -0,0 +1,188 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ memory.c
+
+Abstract:
+
+ Memory management routines.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+
+#define DEFAULT_HEAP_INITIAL_SIZE (4*1024L) // 4K or one page, should be 0 ??
+#define DEFAULT_HEAP_MAXIMUM_SIZE (64*1024L) // upper limit taken from Init_submalloc, better value would be 0 ??
+
+
+DWORD RplMemInit( PHANDLE pMemoryHandle)
+/*++
+
+Routine Description:
+ Initializes memory management
+
+Arguments:
+ pMemoryHandle - pointer to memory handle, in PRIVATE case it means something,
+ in SHARED case it is just initialized to NULL
+
+Return Value:
+ ERROR_SUCCESS if success, else last error
+
+--*/
+{
+ *pMemoryHandle = HeapCreate(
+ 0L,
+ 64 * 1024, // make it large due to shoddy code in Rtl routines
+ 0 // Let it grow without limit !
+ );
+
+ if ( *pMemoryHandle == NULL) {
+ DWORD status = GetLastError();
+ RplDump( ++RG_Assert,( "status=%d", status));
+ return( status);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MemInit: HeapCreate => handle=0x%x", *pMemoryHandle));
+
+ return( ERROR_SUCCESS);
+}
+
+
+
+PVOID RplMemAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD size
+ )
+/*++
+
+Routine Description:
+ Alocates memory object from a memory heap. Memory heap must have a valid handle.
+
+Arguments:
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ size - size of memory object
+
+Return Value:
+ Pointer to newly allocated memory if successful, else NULL.
+
+--*/
+{
+ PVOID ptr;
+ ptr = HeapAlloc( MemoryHandle, 0, size);
+ if ( ptr == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMALLOC,(
+ "MemAlloc( 0x%x, %d) => 0x%x", MemoryHandle, size, ptr));
+ }
+ return( ptr);
+}
+
+
+
+VOID RplMemFree(
+ HANDLE MemoryHandle,
+ PVOID pMemoryObject
+ )
+/*++
+
+Routine Description:
+ Memory object must have been allocated via MemFree() or MemReAlloc().
+ Memory object is freed.
+
+Arguments:
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ pMemoryObject - pointer to memory object
+
+Return Value:
+ None.
+
+--*/
+{
+ if ( !HeapFree( MemoryHandle, 0, pMemoryObject)) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MemFree( 0x%x) <= 0x%x", MemoryHandle, pMemoryObject));
+ }
+}
+
+
+
+VOID RplMemClose( HANDLE MemoryHandle)
+/*++
+
+Routine Description:
+ Destroys the heap thus releasing all the memory allocated with this handle.
+ HeapDestroy() is BOOL, here we just hide dirt under the rug!
+ See more extensive comments under MemFree().
+
+Arguments:
+ MemoryHandle - heap handle
+
+Return Value:
+ None.
+
+--*/
+{
+ if ( !HeapDestroy( MemoryHandle)) {
+ RplDump( ++RG_Assert, ,( "Error=%d", GetLastError()));
+ }
+}
+
+
+
+PVOID RplMemReAlloc(
+ IN HANDLE MemoryHandle,
+ IN DWORD Flags,
+ IN PVOID old_ptr,
+ IN DWORD new_size
+ )
+/*++
+
+Routine Description:
+
+ Reallocates data to a new size, preserving the content.
+
+Arguments:
+
+ MemoryHandle - memory handle (relevant in PRIVATE case only)
+ old_ptr - pointer to memory object (before this call)
+ Flags -
+ new_size - desired new size of memory object
+
+
+Return Value:
+
+ If successful, returns pointer to memory object whose size is (at least)
+ equal to "new_size" and whose first MIN( old_size, new_size) bytes
+ are unchanged compared to corresponding bytes at "old_ptr".
+
+ If unsuccessful, returns NULL.
+
+--*/
+{
+ PVOID ptr;
+ ptr = HeapReAlloc( MemoryHandle, Flags, old_ptr, new_size);
+ if ( ptr == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ } else {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMALLOC,(
+ "MemReAlloc( 0x%x, 0x%x, %d) => 0x%x", MemoryHandle, old_ptr, new_size, ptr));
+ }
+ return( ptr);
+}
diff --git a/private/net/svcdlls/rpl/server/open.c b/private/net/svcdlls/rpl/server/open.c
new file mode 100644
index 000000000..eb4b1c4e0
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/open.c
@@ -0,0 +1,600 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ _open.c
+
+Abstract:
+
+ Initializes workstation specific data structures and returns the handle.
+
+ Provides similar functionality to rmapopen.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "database.h"
+#include "bbcfile.h"
+#include "fitfile.h"
+#include "open.h"
+
+// translates 32 bit physical address to 16-bit paragraphs
+#define PHYS_TO_PARA( physaddr) ((WORD)((physaddr) >> 4))
+
+#define MAX_WKSTA_PROFILES 10 // rplmgr supports now only 2!
+#define PROF_DATA_SIZE (sizeof(MBOOTDATA)+80) // include comment size!
+#define MAX_WKSTA_BUF ((DWORD)0xff00)
+
+
+
+DWORD FillString( OUT PCHAR Buffer, IN PCHAR String)
+{
+ strcpy( Buffer, String);
+ return( strlen( Buffer));
+}
+
+
+BOOL RplCreateWkstaLine(
+ IN OUT PRPL_WORKER_DATA pWorkerData,
+ OUT PDWORD pWkstaLineSize
+ )
+/*++
+ LANMAN 2.1 wksta record in RPL.MAP contained the following information:
+
+ Field 1: AdapterId - NO
+ Field 2: WkstaName
+ Field 3: Username/Password prompting field.
+ Field 4: Fit file name. - NO
+ Field 5: Name of server used to access files for booting. - NO (DLC server)
+ Field 6: Shared or Personal profile. - NO
+ Field 7: '~'
+ Field 8: '~'
+ Field 9: '~'
+ Field 10: ',,,'
+ Field 11: '~'
+ Field 12: Tag of associated LANMAN 2.1 boot block configuration (server) record. - NO
+ Field 13: '~' (but see below)
+ Field 14: Name of associtated profile. - NO
+ Field 15: Descriptive comment. - NO
+ Field 16: Optional IP address of a workstation.
+ Field 17: Optional TCP/IP subnet mask.
+ Field 18: Optional TCP/IP gateway address.
+
+ Out of the above, client side only needs Fields # 2, 3, 15, 16, 17 & 18.
+ All the other fields can be replaced with tildas. And of course if this
+ does not work we can always go back to the original wksta line & send him
+ all then cut back on the amount of info.
+
+ All of these lines worked fine for VLADIMV7 client. Checked "dir c:",
+ logging as vladimv on Redmond, treeconnecting & dir on remote drives.
+
+#define RPLTEST_WKSTALINE "02608C1B87A5 VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOST ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOST ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ VLADIMV3 S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 VLADIMV1~=>~shared~dos~profile ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ S ~ ~ ~ ,,, ~ ~ ~ d5elnk2 ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ,,, ~ ~ ~ d5elnk2 ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ,,, ~ ~ ~ ~ ~ ~ ~ ~"
+#define RPLTEST_WKSTALINE "~ VLADIMV7 N ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~"
+Field 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
+
+More changes:
+ Client side software RPLBOOT.SYS has been changed to receive value for
+ TCPIP_NO_DHCP macro in Field 13 above. Sending value 1 will disable DHCP
+ on the client. Sending value 0 will enable DCHP on the client. But the
+ value of this field matters only if TCPIP is enabled for this client by
+ appropriate changes in boot block configuration file, config.sys and
+ autoexec.bat that this client uses.
+--*/
+{
+#define BLANK_FIELD "~ "
+#define COMMON_FIELD_LENGTH 2 // except for WkstaName & TcpIp strings
+#define MAX_TCPIP_ADDRESS_LENGTH 15
+
+ CHAR Buffer[ 14 * COMMON_FIELD_LENGTH + MAX_COMPUTERNAME_LENGTH + 1
+ + 3 * (MAX_TCPIP_ADDRESS_LENGTH + 1) + 1];
+ DWORD Length;
+ DWORD ByteCount;
+ DWORD Index;
+
+ //
+ // Field 1:
+ //
+ Length = FillString( Buffer, BLANK_FIELD);
+
+ //
+ // Field 2:
+ //
+ ByteCount = WideCharToMultiByte( // this counts the terminal null byte
+ CP_OEMCP,
+ 0,
+ pWorkerData->WkstaName,
+ -1, // WkstaName is null terminated
+ Buffer + Length, // output buffer
+ MAX_COMPUTERNAME_LENGTH + 1, // output buffer size
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( ByteCount == 0) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaInternal;
+ return( FALSE);
+ }
+ Length += ByteCount - 1;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Field 3:
+ //
+ Buffer[ Length++] = (char)pWorkerData->LogonInput;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Fields 4-12:
+ //
+ for ( Index = 4; Index <= 12; Index++) {
+ Length += FillString( Buffer + Length, BLANK_FIELD);
+ }
+
+ //
+ // Field 13:
+ //
+ Buffer[ Length++] = pWorkerData->DisableDhcp;
+ Buffer[ Length++] = ' ';
+
+ //
+ // Fields 14-15:
+ //
+ for ( Index = 14; Index <= 15; Index++) {
+ Length += FillString( Buffer + Length, BLANK_FIELD);
+ }
+
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpAddress);
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpSubnet);
+ Length += FillTcpIpString( Buffer + Length, pWorkerData->TcpIpGateway);
+
+ Buffer[ --Length] = 0; // overwrite last space
+
+//#define RPL_ELNK
+#ifdef RPL_ELNK
+ if ( !wcscmp( pWorkerData->pRcb->AdapterName, L"02608C0A9B37")) {
+ strcpy( Buffer, "02608C0A9B37 ELNK N fits\\dos500 VLADIMV3 S ~ ~ ~ ,,, ~ RDOSL ~ DOS500L ~ ~ ~ ~ ");
+ Length = strlen( Buffer);
+ }
+#endif
+
+ pWorkerData->WkstaLine = RplMemAlloc( pWorkerData->MemoryHandle, Length + 1);
+ if ( pWorkerData->WkstaLine == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+ memcpy( pWorkerData->WkstaLine, Buffer, Length + 1);
+ *pWkstaLineSize = Length + 1;
+ return( TRUE);
+}
+
+
+BOOL RplMakeWkstaBuf( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+
+ Procedure allocates and initializes bblock header and wksta specific
+ data. pWorkerData->wksta_buf will be set to point to this buffer.
+
+ Procedure:
+ - allocates and shrinks the file index table
+ - setup the boot block header and saves its static parameters
+ - makes the file list table and saves the parameters of DOS EXE/SYS/COMs
+ - builds the boot data for RPL MFSD
+ - fixes up the 32 bits physical boot block base and file block base addr
+ - copies and binds the resource table to the memory
+ - binds the files in the files list to the PC memory
+
+Arguments:
+
+Return Value:
+ TRUE if successful.
+ FALSE if unsuccessful (then we usually set termination event also).
+
+--*/
+{
+ DWORD index;
+ PBOOT_DATA pBootData; // ptr to boot data structure used by RPL MFSD
+ PRESOURCE_TBL resource_tbl; // OS2LDR parameters
+ PBOOT_BLOCK_HDR pBBH; // ptr to boot block header
+ DWORD wbuf_i; // running offset from boot block header
+ PBYTE pBuf; // a misc string
+ DWORD resource_tbl_len; //
+ DWORD WkstaLineSize; // for old style rpl.map wksta line
+ PBYTE wksta_buf; // space for the wksta specific data
+ DWORD wksta_buf_len; // length of wksta specific data
+ DWORD offData;
+ DWORD cbBootDataSize;
+ //
+ // boot_block_base is offset to the boottom of the lowest file in memory
+ //
+ DWORD boot_block_base = pWorkerData->boot_block_base;
+ DWORD file_block_base = pWorkerData->file_block_base;
+ PFLIST_TBL pFlistTbl = pWorkerData->flist_tbl;
+ DWORD lenFlistTbl = pWorkerData->flist_tbl_len;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++MakeWkstaBuf(0x%x", pWorkerData));
+
+ if ( !RplFitFile( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Clients still expect old style (rpl.map) wksta line.
+ //
+ if ( !RplCreateWkstaLine( pWorkerData, &WkstaLineSize)) {
+ return( FALSE);
+ }
+
+ cbBootDataSize =
+ sizeof(BOOT_DATA) + // size of RPL MiniFSD parameters
+ WkstaLineSize + // wksta record size, DBCS data
+ pWorkerData->ClientFitSize + // FIT file image size, DBCS data
+#ifdef RPL_ELNK
+#define RPL_ELNK_7_SPACES " "
+ sizeof( RPL_ELNK_7_SPACES) + // Pad with spaces after wksta line.
+ 1 + // Include 0x1A at the end of fit file.
+#endif
+ RG_CodePageSize; // code page size
+
+ wksta_buf_len =
+ cbBootDataSize +
+ pWorkerData->min_wksta_buf +
+ sizeof(BOOT_BLOCK_HDR) + // size of standard header structure
+ PATHLEN + 1 + // space for UNC name of DOS FAT hdr
+ MAX_WKSTA_PROFILES * PROF_DATA_SIZE; // max size of multiboot data
+
+
+ if ( wksta_buf_len > MAX_WKSTA_BUF) {
+ RplDump( ++RG_Assert, ( "wksta_buf_len=%d", wksta_buf_len));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+
+ //
+ // allocate wksta specific data, set the header base pointer and
+ // the base offset of dynamic data (wbuf_i)
+ //
+
+ // DbgUserBreakPoint(); // for debugging
+
+ pBBH = RplMemAlloc( pWorkerData->MemoryHandle, wksta_buf_len );
+ if( pBBH == NULL) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaMemory;
+ return( FALSE);
+ }
+
+ wksta_buf = (PBYTE)pBBH;
+ memset( wksta_buf, 0, wksta_buf_len);
+ wbuf_i = sizeof(BOOT_BLOCK_HDR) + (lenFlistTbl - 1) * sizeof(FILE_DATA);
+ //
+ // "id_stamp" is the first byte of boot block sent to the client
+ //
+ pBBH->id_stamp = 0x18cd; // reboot with int 18, if called
+ pBBH->usInfoLevel = 1; // version number of boot block
+ pBBH->file_list_len = (BYTE)lenFlistTbl;
+
+ //
+ // Copy the file names and parameters of the files in the boot block.
+ //
+
+ for ( index = 0; index < lenFlistTbl; index++) {
+
+ pBBH->aFileData[ index] = pFlistTbl[ index].FileData;
+ //
+ // Copy the file names that were saved in pFlistTbl element.
+ //
+ pBBH->aFileData[ index].name_offset = (WORD)wbuf_i;
+
+ memcpy( wksta_buf + wbuf_i, pFlistTbl[ index].DbcsFileName, pFlistTbl[ index].DbcsFileNameSize);
+ wbuf_i += pFlistTbl[ index].DbcsFileNameSize;
+
+ // Copy data from Boot Block Configuration file.
+
+ pBBH->aFileData[ index].param_offset = (WORD)wbuf_i;
+
+ //
+ // exe/com/sys files must have MS-DOS parameter format
+ //
+ if ((pFlistTbl[ index].FileData.file_type & OTHER_FILES_MASK) == 0) {
+ //
+ // Get length of EXE/SYS/COM DOS parameters.
+ //
+ BYTE length = pFlistTbl[ index].FileData.param_len;
+
+ if ( length) {
+ wksta_buf[ wbuf_i++] = (BYTE)( length + 1);
+ wksta_buf[ wbuf_i++] = ' '; // space before params
+ memcpy( wksta_buf + wbuf_i, pFlistTbl[ index].param_list, length);
+ wbuf_i += length;
+ wksta_buf[ wbuf_i++] = 0xD;
+ wksta_buf[ wbuf_i++] = 0xA;
+ wksta_buf[ wbuf_i++] = 0; // null terminated
+ pBBH->aFileData[ index].param_len = (BYTE)( length + 4);
+ } else {
+ wksta_buf[ wbuf_i++] = 0;
+ if ( pFlistTbl[ index].FileData.file_type & IS_EXECUTABLE_FILE) {
+ //
+ // For device drivers with no parameters, the parameter
+ // line is terminated by 0xA not 0xD,0xA
+ // BUGBUG Should param_len for them below be 2 instead of 3 ?
+ //
+ wksta_buf[ wbuf_i++] = 0xD;
+ }
+ wksta_buf[ wbuf_i++] = 0xA;
+ wksta_buf[ wbuf_i++] = 0; // null terminated
+ pBBH->aFileData[ index].param_len = 3;
+ }
+ }
+ }
+
+ //
+ // Set up the resource table. Here we copy data that describes the
+ // loader line in Boot Block Configuration file.
+ //
+ resource_tbl = (PRESOURCE_TBL)(wksta_buf + wbuf_i);
+ pBBH->offResourceTbl = (WORD)wbuf_i;
+ pBBH->cbResourceTbl = (WORD)pWorkerData->resource_tbl_size;
+ wbuf_i += pWorkerData->resource_tbl_size;
+ memcpy( resource_tbl, pWorkerData->resource_tbl, pWorkerData->resource_tbl_size);
+
+ //
+ // Round up the boot data offset (resource table format requires it).
+ //
+ wbuf_i = (wbuf_i + 15) & 0xfff0;
+ pBBH->offBootData = (WORD)wbuf_i;
+ pBootData = (PBOOT_DATA)(wksta_buf + wbuf_i);
+ pBootData->cbSize = (WORD)cbBootDataSize; // alignment OK
+ pBuf = wksta_buf + wbuf_i;
+
+ // offData will be used to walk & copy data structures mentioned
+ // in cbBootDataSize formula above.
+
+ offData = sizeof( BOOT_DATA); // initialize
+
+ //
+ // Copy the wksta record line from RPL.map (dbcs string).
+ //
+ strcpy( pBuf + offData, pWorkerData->WkstaLine);
+
+ pBootData->offWkstaLine = (WORD)offData;
+ pBootData->cbWkstaLine = (WORD)WkstaLineSize;
+ pBBH->offRplWkstaLine = (WORD)(wbuf_i + offData);
+ offData += WkstaLineSize;
+
+#ifdef RPL_ELNK
+ //
+ // Pad with spaces after wksta line.
+ //
+ memcpy( pBuf + offData, RPL_ELNK_7_SPACES, sizeof( RPL_ELNK_7_SPACES));
+ offData += sizeof( RPL_ELNK_7_SPACES);
+ pBootData->cbWkstaLine += sizeof( RPL_ELNK_7_SPACES);
+#endif
+
+ // Copy the FIT file image.
+
+ memcpy( pBuf + offData, pWorkerData->ClientFit, pWorkerData->ClientFitSize);
+ pBootData->offFit = (WORD)offData;
+ pBootData->cbFit = (WORD)pWorkerData->ClientFitSize;
+ offData += pWorkerData->ClientFitSize;
+#ifdef RPL_ELNK
+ //
+ // Append 0x1A at the end of fit file.
+ //
+ *( pBuf + offData - 1) = 0x1A;
+ *( pBuf + offData) = 0;
+ offData++;
+ pBootData->cbFit++;
+#endif
+
+ // Copy the code page.
+
+ memcpy( pBuf + offData, RG_CodePageBuffer, RG_CodePageSize);
+ pBootData->cbCodePage = (WORD)RG_CodePageSize;
+ pBootData->offCodePage = (WORD)offData;
+
+ wbuf_i += cbBootDataSize;
+ pBBH->offMBootData = (WORD)wbuf_i;
+
+ //
+ // Send no multiboot data - i.e. send NULL multiboot data by setting the
+ // size field of the first record to zero.
+
+#ifdef RPL_ELNK
+ if ( !wcscmp( pWorkerData->pRcb->AdapterName, L"02608C0A9B37")) {
+ MBOOTDATA UNALIGNED * pMbootData = (wksta_buf + wbuf_i);
+ pMbootData->cbSize = 0x2a;
+ strcpy( pMbootData->achProfile, "DOS500L");
+ strcpy( pMbootData->achProfileComment, "DOS 5.00 3Com Etherlink");
+ wbuf_i += 0x2a;
+ wbuf_i++; // or RPLBOOT.SYS would begin at 24e
+ } else {
+ *(WORD UNALIGNED *)(wksta_buf + wbuf_i) = 0;
+ wbuf_i += sizeof(WORD);
+ }
+#else
+ *(WORD UNALIGNED *)(wksta_buf + wbuf_i) = 0;
+ wbuf_i += sizeof(WORD);
+#endif
+
+
+ //
+ // DON'T SAVE ANY MORE DATA TO WKSTA BUFFER, WE MAY BE OUT OF MEMORY
+ // (do it before RplBuildMultiBootData)
+ //
+
+ //
+ // Set up the final size.
+ //
+ wksta_buf_len = (wbuf_i + 15) & 0xfff0;
+ pBBH->cbSize = (WORD)wksta_buf_len;
+
+ //
+ // FIX UP AND CHECK THE BOOT BLOCK TO THE WORKSTATION MEMORY !!!
+ // get the base address of the file list and boot block
+ //
+
+ if ( file_block_base == 0) {
+
+ // no ORG type line in BB.CFG file
+
+ if (boot_block_base == 0) {
+
+ // no BASE type line in BB.CFG file
+
+ boot_block_base = MIN_BBLOCK_BASE;
+ }
+ file_block_base = boot_block_base + wksta_buf_len;
+
+ } else if (boot_block_base == 0) {
+
+ boot_block_base = file_block_base - wksta_buf_len;
+ }
+
+ // Verify that the boot block is valid:
+ // - there must be space for wksta specific data in the boot block
+ // - boot block base must be above or equal the minimum value
+
+ if ( file_block_base < boot_block_base + wksta_buf_len
+ || boot_block_base < MIN_BBLOCK_BASE) {
+ RplDump( ++RG_Assert, (
+ "file_block_base=%d boot_block_base=%d wksta_buf_len=%d",
+ file_block_base, boot_block_base, wksta_buf_len));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = pWorkerData->BbcFile;
+ pWorkerData->EventId = NELOG_Files_Dont_Fit;
+ return( FALSE);
+ }
+
+ //
+ // Get the resource table pointer (table is in wksta specific data),
+ // decrement length, because BootData is not included
+
+ if ( pWorkerData->loader_i != INVALID_FILE_OFFSET) {
+
+ // locate segments of resource table
+
+ resource_tbl_len = resource_tbl->entries - 1;
+ for ( index = 0; index < resource_tbl_len; index++) {
+
+ resource_tbl->file_tbl[ index].pos_in_paras
+ += PHYS_TO_PARA( file_block_base);
+ }
+
+ // the boot data must be in the resource table,
+
+ resource_tbl->file_tbl[ index].pos_in_paras
+ = PHYS_TO_PARA( boot_block_base + pBBH->offBootData);
+ resource_tbl->file_tbl[ index].file_len = cbBootDataSize;
+ }
+
+ // locate the boot block files to the PC memory
+
+ for ( index = 0; index < lenFlistTbl; index++) {
+ pBBH->aFileData[ index].file_addr += file_block_base;
+ }
+
+ //
+ // Do not bother to reallocate wksta_buf to its new smaller size.
+ // This would be a waste of time since wksta_buf will be freed
+ // when we are done with booting this client.
+ //
+
+ pWorkerData->wksta_buf = wksta_buf;
+ pWorkerData->wksta_buf_len = (WORD)wksta_buf_len;
+
+ //
+ // get file block base offset relative from the base of the boot block
+ //
+
+ pWorkerData->fblock_base_offset = file_block_base - boot_block_base;
+
+ pWorkerData->base_address = boot_block_base;
+ pWorkerData->jump_address = pBBH->aFileData[pWorkerData->rplboot_i].file_addr;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--MakeWkstaBuf(0x%x", pWorkerData));
+ return( TRUE);
+
+} // RplMakeWkstaBuf
+
+
+
+BOOL RplOpenData( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ Opens rpl.map data for reading by a workstation thread.
+
+Arguments:
+ pWorkerData - worker data
+
+Return Value:
+ TRUE if success, else FALSE.
+
+--*/
+{
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++OpenData(0x%x", pWorkerData));
+
+ //
+ // Retrive wksta database info.
+ //
+ if ( !RplWorkerFillWksta( pWorkerData)) {
+ if ( pWorkerData->EventId == NO_ERROR) {
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NERR_RplWkstaInfoCorrupted;
+ }
+ return( FALSE);
+ }
+
+ //
+ // Process boot block configuration file.
+ //
+ if ( !RplBbcFile( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Make wksta specific data buffer.
+ //
+ if ( !RplMakeWkstaBuf( pWorkerData)) {
+ return( FALSE);
+ }
+
+ //
+ // Initialize file list table (includes all downloaded files)
+ //
+
+ pWorkerData->pDataBuffer = NULL;
+ pWorkerData->cur_flist_i = 0;
+ pWorkerData->cur_offset = INVALID_FILE_OFFSET;
+ pWorkerData->cur_file_base_offset = INVALID_FILE_OFFSET;
+ pWorkerData->is_end_of_bblock = FALSE;
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--OpenData(0x%x", pWorkerData));
+ return( TRUE);
+}
diff --git a/private/net/svcdlls/rpl/server/open.h b/private/net/svcdlls/rpl/server/open.h
new file mode 100644
index 000000000..f9cd7e545
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/open.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ open.h
+
+Abstract:
+
+ Exports from open.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplOpenData( IN OUT PRPL_WORKER_DATA pWorkerData);
+
+
diff --git a/private/net/svcdlls/rpl/server/profile.c b/private/net/svcdlls/rpl/server/profile.c
new file mode 100644
index 000000000..7cc782f29
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/profile.c
@@ -0,0 +1,1139 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ profile.c
+
+Abstract:
+
+ Profile APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#include "config.h"
+#include "wksta.h"
+#define RPLPROFILE_ALLOCATE
+#include "profile.h"
+#undef RPLPROFILE_ALLOCATE
+
+#define PROFILE_FLAGS_DISK_PRESENT_TRUE ((DWORD)0x00000001)
+#define PROFILE_FLAGS_DISK_PRESENT_FALSE ((DWORD)0x00000002)
+#define PROFILE_FLAGS_MASK_DISK_PRESENT \
+ ( PROFILE_FLAGS_DISK_PRESENT_FALSE | \
+ PROFILE_FLAGS_DISK_PRESENT_TRUE )
+
+
+
+DWORD ProfileGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case PROFILE_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplProfileInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplProfileInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD ProfileGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = ProfileGetField( pSession, PROFILE_FitPersonal, &Info->FitPersonal, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_FitShared, &Info->FitShared, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = ProfileGetField( pSession, PROFILE_ConfigName, &Info->ConfigName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = ProfileGetField( pSession, PROFILE_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = ProfileGetField( pSession, PROFILE_ProfileComment, &Info->ProfileComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( ProfileName == NULL) {
+ Error = ProfileGetField( pSession, PROFILE_ProfileName, &Info->ProfileName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ Info->ProfileName = MIDL_user_allocate( DataSize);
+ if ( Info->ProfileName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, ( "ProfileName=0x%x", Info->ProfileName));
+ memcpy( Info->ProfileName, ProfileName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID ProfileGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+
+ if ( Info == NULL) {
+ return;
+ }
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ MIDL_user_free( Info->FitPersonal);
+ }
+ if ( Info->FitShared != NULL) {
+ MIDL_user_free( Info->FitShared);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ if ( Info->ConfigName != NULL) {
+ MIDL_user_free( Info->ConfigName);
+ }
+ NOTHING; // fall through
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ProfileComment != NULL) {
+ MIDL_user_free( Info->ProfileComment);
+ }
+ if ( Info->ProfileName != NULL) {
+ MIDL_user_free( Info->ProfileName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR AdapterName,
+ IN OUT LPRPL_PROFILE_ENUM ProfileEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ RPL_FILTER Filter;
+ PRPL_FILTER pFilter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( ProfileEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_2);
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitPersonal
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitShared
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ConfigName
+ NOTHING; // fall through
+ case 1:
+ if ( ProfileEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_1);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( ProfileEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_PROFILE_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of ProfileComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ProfileName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( AdapterName != NULL) {
+ pFilter = &Filter;
+ if ( !ValidHexName( AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ AdapterName[ 6] = 0; // truncate all but first 6 digits
+ pFilter->VendorId = wcstoul( AdapterName, NULL, 16);
+ pFilter->FindFirst = TRUE;
+ } else {
+ pFilter = NULL;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, (
+ "ProfileEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ ProfileEnum->ProfileInfo.Level0->Buffer = (LPRPL_PROFILE_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, PROFILE_TABLE_TAG, pFilter, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = ProfileGetInfo( pSession, NULL, ProfileEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->ProfileTableId, pFilter, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ ProfileGetInfoCleanup( ProfileEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ ProfileGetInfoCleanup( ProfileEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_PROFILE, ("ProfileEnum: EntriesRead = 0x%x", EntriesRead));
+
+ ProfileEnum->ProfileInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ ProfileEnum->ProfileInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, pFilter,
+ ((LPRPL_PROFILE_INFO_0)(Buffer-CoreSize))->ProfileName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ OUT LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct
+ )
+{
+ DWORD Error;
+ LPBYTE Buffer;
+ INT SpaceLeft;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ProfileName);
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_0));
+ ProfileInfoStruct->ProfileInfo0 = (LPRPL_PROFILE_INFO_0)Buffer;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_1));
+ ProfileInfoStruct->ProfileInfo1 = (LPRPL_PROFILE_INFO_1)Buffer;
+ break;
+ case 2:
+ Buffer = MIDL_user_allocate( sizeof( RPL_PROFILE_INFO_2));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_PROFILE_INFO_2));
+ ProfileInfoStruct->ProfileInfo2 = (LPRPL_PROFILE_INFO_2)Buffer;
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ } else {
+ Error = ProfileGetInfo( pSession, ProfileName, Level, Buffer, &SpaceLeft);
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != NO_ERROR) {
+ ProfileGetInfoCleanup( Level, Buffer);
+ switch( Level) {
+ case 0:
+ MIDL_user_free( Buffer);
+ ProfileInfoStruct->ProfileInfo0 = NULL;
+ break;
+ case 1:
+ MIDL_user_free( Buffer);
+ ProfileInfoStruct->ProfileInfo1 = NULL;
+ break;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD ProfileSetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ IN LPVOID Data,
+ IN DWORD DataSize
+ )
+{
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ FieldIndex].ColumnId, Data, DataSize, 0, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD ProfileSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->FitPersonal != NULL) {
+ *pErrorParameter = PROFILE_FitPersonal;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_FitPersonal].ColumnId,
+ Info->FitPersonal,
+ ( wcslen( Info->FitPersonal) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitShared != NULL) {
+ *pErrorParameter = PROFILE_FitShared;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_FitShared].ColumnId,
+ Info->FitShared,
+ ( wcslen( Info->FitShared) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = PROFILE_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ConfigName != NULL) {
+ *pErrorParameter = PROFILE_ConfigName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ConfigName].ColumnId,
+ Info->ConfigName,
+ ( wcslen( Info->ConfigName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->Flags != 0) {
+ *pErrorParameter = PROFILE_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->ProfileComment != NULL) {
+ *pErrorParameter = PROFILE_ProfileComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ProfileComment].ColumnId,
+ Info->ProfileComment,
+ ( wcslen( Info->ProfileComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->ProfileName != NULL) {
+ *pErrorParameter = PROFILE_ProfileName;
+ CallM( JetSetColumn( pSession->SesId, pSession->ProfileTableId,
+ ProfileTable[ PROFILE_ProfileName].ColumnId,
+ Info->ProfileName,
+ ( wcslen( Info->ProfileName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN DWORD Level,
+ IN LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+/*++
+ If parameter pointer is NULL, then we do not change such parameters.
+ If parameter non-pointer has a special value, then we do not change
+ such parameters.
+
+ Some parameters cannot be changed for now. If caller deos not specify
+ a no-change value for such a parameter, we print a warning message &
+ pretend we received a no change value.
+--*/
+{
+ DWORD Error;
+ DWORD SpaceLeft;
+ DWORD ErrorParameter;
+ LPVOID Buffer;
+ LPRPL_PROFILE_INFO_2 Info;
+ PWCHAR ConfigName;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ ConfigName = NULL;
+ _wcsupr( ProfileName);
+
+ Info = Buffer = ProfileInfoStruct->ProfileInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_BootName;
+ break;
+ }
+ if ( Info->BootName != NULL) {
+ _wcsupr( Info->BootName);
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_ConfigName;
+ break;
+ }
+ if ( Info->ConfigName != NULL) {
+ _wcsupr( Info->ConfigName);
+ }
+ NOTHING; // fall through
+ case 1:
+ switch( Info->Flags) {
+ case PROFILE_FLAGS_DISK_PRESENT_TRUE:
+ case PROFILE_FLAGS_DISK_PRESENT_FALSE:
+ Info->Flags = 0;
+ NOTHING; // fall through
+ case 0:
+ break;
+ default:
+ ErrorParameter = PROFILE_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( RPL_STRING_TOO_LONG( Info->ProfileComment)) {
+ ErrorParameter = PROFILE_ProfileComment;
+ break;
+ }
+ if ( Info->ProfileName != NULL) {
+ _wcsupr( Info->ProfileName);
+ if ( wcscmp( Info->ProfileName, ProfileName)) {
+ RplDump( ++RG_Assert, ("Change of ProfileName not supported yet."));
+ ErrorParameter = PROFILE_ProfileName;
+ break;
+ }
+ MIDL_user_free( Info->ProfileName);
+ Info->ProfileName = NULL;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+ ErrorParameter = 0;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+
+ if ( Info->ConfigName != NULL) {
+ Error = ProfileGetField( pSession, PROFILE_ConfigName, &ConfigName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( wcscmp( Info->ConfigName, ConfigName)) {
+ RplDump( ++RG_Assert, ("Change of ConfigName not supported yet."));
+ ErrorParameter = PROFILE_ConfigName;
+ Error = ERROR_INVALID_PARAMETER;
+ goto cleanup;
+ }
+ MIDL_user_free( Info->ConfigName);
+ Info->ConfigName = NULL;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+
+ Error = ProfileSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL));
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( ConfigName != NULL) {
+ MIDL_user_free( ConfigName);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD AddFileExtension(
+ IN PWCHAR * pFilePath,
+ IN PWCHAR FileExtension,
+ IN BOOLEAN ExtensionOK
+ )
+{
+#define DOT_CHAR L'.'
+#define BACK_SLASH_CHAR L'\\'
+ PWCHAR FilePathEx;
+ PWCHAR pDot;
+ DWORD Length;
+
+ if ( *pFilePath == NULL) {
+ RPL_RETURN( NERR_RplConfigInfoCorrupted);
+ }
+
+ pDot = wcsrchr( *pFilePath, DOT_CHAR);
+ if ( pDot != NULL) {
+ //
+ // Found a DOT. FilePath may have an extension.
+ //
+ if ( wcschr( pDot, BACK_SLASH_CHAR) == NULL) {
+ //
+ // There is no backslash after the DOT. FilePath has an
+ // extension. Return error if caller insists that file
+ // should have no extension.
+ //
+ if ( !ExtensionOK) {
+ RPL_RETURN( NERR_RplConfigInfoCorrupted);
+ }
+ return( NO_ERROR);
+ }
+ }
+ Length = wcslen( *pFilePath) + wcslen( FileExtension);
+ FilePathEx = MIDL_user_allocate( (Length + 1) * sizeof(WCHAR));
+ if ( FilePathEx == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ wcscpy( FilePathEx, *pFilePath);
+ wcscat( FilePathEx, FileExtension);
+ MIDL_user_free( *pFilePath);
+ *pFilePath = FilePathEx;
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_PROFILE_INFO_STRUCT ProfileInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_PROFILE_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ BOOL FreeBootName = FALSE;
+ BOOL FreeFitShared = FALSE;
+ BOOL FreeFitPersonal = FALSE;
+ PWCHAR DirName = NULL;
+ PWCHAR DirName2 = NULL;
+ PWCHAR DirName3 = NULL;
+ PWCHAR DirName4 = NULL;
+ JET_ERR JetError;
+ BOOL DiskDelete = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = ProfileInfoStruct->ProfileInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitPersonal, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitPersonal;
+ break;
+ }
+ if ( !ValidName( Info->FitShared, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_FitShared;
+ break;
+ }
+ if ( !ValidName( Info->BootName, RPL_MAX_BOOT_NAME_LENGTH, FALSE)) {
+ ErrorParameter = PROFILE_BootName;
+ break;
+ }
+ if ( Info->BootName != NULL) {
+ _wcsupr( Info->BootName);
+ }
+ if ( !ValidName( Info->ConfigName, RPL_MAX_CONFIG_NAME_LENGTH, TRUE)) {
+ ErrorParameter = PROFILE_ConfigName;
+ break;
+ }
+ _wcsupr( Info->ConfigName);
+ if ( Info->Flags != 0) {
+ ErrorParameter = PROFILE_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->ProfileComment)) {
+ ErrorParameter = PROFILE_ProfileComment;
+ break;
+ }
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ ErrorParameter = PROFILE_ProfileName;
+ break;
+ }
+ _wcsupr( Info->ProfileName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, CONFIG_TABLE_TAG, Info->ConfigName)) {
+ Error = NERR_RplConfigNotFound;
+ goto cleanup;
+ }
+ // BUGBUG Should make sure CONFIG is enabled
+ if ( Info->BootName == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_BootName, &Info->BootName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ }
+ if ( Info->FitShared == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_FitShared, &Info->FitShared, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitShared = TRUE;
+ Error = AddFileExtension( &Info->FitShared, L".FIT", TRUE);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+ if ( Info->FitPersonal == NULL) {
+ Error = ConfigGetField( pSession, CONFIG_FitPersonal, &Info->FitPersonal, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitPersonal = TRUE;
+ Error = AddFileExtension( &Info->FitPersonal, L".FIT", TRUE);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName, &DirName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName2, &DirName2, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName3, &DirName3, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Error = ConfigGetField( pSession, CONFIG_DirName4, &DirName4, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // The call to ProfileSetInfo() will add PROFILE record to the database.
+ // The call may succeed but we may still fail to do the treecopy
+ // operations below. We can detect this situation any time later on
+ // by the value of the Flags bit.
+ //
+ Info->Flags = PROFILE_FLAGS_DISK_PRESENT_FALSE;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepInsert));
+
+ Error = ProfileSetInfo( pSession, 2, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ErrorParameter = 0;
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError == JET_errKeyDuplicate) {
+ Error = NERR_RplProfileNameUnavailable;
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+
+ DiskDelete = TRUE; // just in case we fail before we are done
+ Error = ProfileDiskAdd( TRUE, Info->ProfileName, DirName, DirName2,
+ DirName3, DirName4);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info->ProfileName)) {
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+ Info->Flags = PROFILE_FLAGS_DISK_PRESENT_TRUE;
+ Error = ProfileSetField( pSession, PROFILE_Flags, &Info->Flags, sizeof(Info->Flags));
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ DiskDelete = FALSE; // success, make sure we do not delete disk data
+
+cleanup:
+ if ( DiskDelete == TRUE) {
+ //
+ // ProfileDiskAdd deletion code does not care about DirName*
+ //
+ ProfileDiskAdd( FALSE, Info->ProfileName, NULL, NULL, NULL, NULL);
+ }
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( Info->BootName);
+ Info->BootName = NULL;
+ }
+ if ( FreeFitShared == TRUE) {
+ MIDL_user_free( Info->FitShared);
+ Info->FitShared = NULL;
+ }
+ if ( FreeFitPersonal == TRUE) {
+ MIDL_user_free( Info->FitPersonal);
+ Info->FitPersonal = NULL;
+ }
+ if ( DirName != NULL) {
+ MIDL_user_free( DirName);
+ }
+ if ( DirName2 != NULL) {
+ MIDL_user_free( DirName2);
+ }
+ if ( DirName3 != NULL) {
+ MIDL_user_free( DirName3);
+ }
+ if ( DirName4 != NULL) {
+ MIDL_user_free( DirName4);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName
+ )
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( ProfileName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( RplFindByField( pSession, WKSTA_TABLE_TAG,
+ WKSTA_INDEX_ProfileNameWkstaName, ProfileName)) {
+ //
+ // We found a WKSTA record which uses this PROFILE.
+ //
+ Error = NERR_RplProfileNotEmpty;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->ProfileTableId));
+ ProfileDiskAdd( FALSE, ProfileName, NULL, NULL, NULL, NULL);
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplProfileClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR SourceProfileName,
+ IN LPWSTR TargetProfileName,
+ IN LPWSTR TargetProfileComment
+ )
+{
+ RPL_PROFILE_INFO_2 Info;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ PWCHAR SaveProfileName;
+ PWCHAR SaveProfileComment;
+ JET_ERR JetError;
+ BOOL DiskDelete = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( SourceProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( SourceProfileName);
+ if ( !ValidName( TargetProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetProfileName);
+ if ( RPL_STRING_TOO_LONG( TargetProfileComment)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Zero all the pointers so we can safely call ProfileGetInfoCleanup().
+ // in all the cases.
+ //
+ memset( &Info, 0, sizeof( Info));
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, SourceProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ Error = ProfileGetInfo( pSession, SourceProfileName, 2, &Info, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Save source data, then overload target data.
+ //
+ SaveProfileName = Info.ProfileName;
+ SaveProfileComment = Info.ProfileComment;
+ Info.ProfileName = TargetProfileName;
+ Info.ProfileComment = TargetProfileComment;
+
+ //
+ // The call to ProfileSetInfo() will add PROFILE record to the database.
+ // The call may succeed but we may still fail to do the treecopy
+ // operations below. We can detect this situation any time later on
+ // by the value of the Flags bit.
+ //
+ Info.Flags = PROFILE_FLAGS_DISK_PRESENT_FALSE;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepInsert));
+
+ Error = ProfileSetInfo( pSession, 2, &Info, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ ErrorParameter = 0;
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ if ( JetError == JET_errKeyDuplicate) {
+ Error = NERR_RplProfileNameUnavailable;
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ }
+ goto cleanup;
+ }
+
+ DiskDelete = TRUE; // just in case we fail before we are done
+ Error = ProfileDiskClone( TRUE, SourceProfileName, TargetProfileName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, TargetProfileName)) {
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->ProfileTableId, JET_prepReplace));
+ Info.Flags = PROFILE_FLAGS_DISK_PRESENT_TRUE;
+ Error = ProfileSetField( pSession, PROFILE_Flags, &Info.Flags, sizeof(Info.Flags));
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ JetError = JetUpdate( pSession->SesId, pSession->ProfileTableId, NULL, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ Error = NERR_RplInternal;
+ goto cleanup;
+ }
+ DiskDelete = FALSE; // success, make sure we do not delete disk data
+
+cleanup:
+ if ( DiskDelete == TRUE) {
+ //
+ // Delete new tree. We do not need to delete the new record
+ // since we are going to rollback the transaction.
+ //
+ ProfileDiskClone( FALSE, NULL, TargetProfileName);
+ }
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ //
+ // Restore source data, then release it.
+ //
+ Info.ProfileName = SaveProfileName;
+ Info.ProfileComment = SaveProfileComment;
+ ProfileGetInfoCleanup( 2, &Info);
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/read.c b/private/net/svcdlls/rpl/server/read.c
new file mode 100644
index 000000000..b47a9af0f
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/read.c
@@ -0,0 +1,409 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ _read.c
+
+Abstract:
+
+ Module reads next send packet from a VIRTUAL boot block.
+ THIS IS VERY COMPLICATED ALGORITHM, YOU MUST UNDERSTAND THE ALGORITHM
+ AND KNOW THE SIDE EFFECTS IF YOU DO ANY CHANGES.
+
+ Provides similar functionality to rmapread.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "read.h"
+
+
+VOID RplMakePatch(
+ IN PRPL_WORKER_DATA pWorkerData,
+ OUT PBYTE send_buffer,
+ IN DWORD read_len,
+ IN DWORD read_offset
+ )
+/*++
+
+Routine Description:
+ Makes NLS patch to send buffer.
+
+Arguments:
+ send_buffer - send buffer to be sent to DLL
+ read_len - length of buffer
+ read_offset - offset of buffer in bblock image
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD Length;
+ DWORD PatchOffset;
+ PBYTE Source;
+ PBYTE Target;
+ DWORD base_offset;
+
+ PatchOffset = pWorkerData->PatchOffset + pWorkerData->fblock_base_offset;
+
+ // if top 1 less that bottom 2 or bottom 1 greater that top 2
+ // => blocks do not overlap and there is nothing to patch here
+
+ if ( !(PatchOffset + DBCS_MESSAGE_BUFFER_SIZE < read_offset ||
+ PatchOffset > read_offset + read_len)) {
+
+ // blocks overlap, get copy address and length,
+ // read offset is MAX( bottom1, bottom2 )
+
+ if (read_offset < PatchOffset) {
+ Source = RG_DbcsMessageBuffer;
+ Target = send_buffer + (WORD)(PatchOffset - read_offset);
+ base_offset = PatchOffset;
+ } else {
+ Source = RG_DbcsMessageBuffer + (WORD)(read_offset - PatchOffset);
+ Target = send_buffer;
+ base_offset = read_offset;
+ }
+
+ // end offset is MIN( top1, top2 )
+ if (read_offset + read_len < PatchOffset + DBCS_MESSAGE_BUFFER_SIZE) {
+ Length = read_offset + read_len - base_offset;
+ } else {
+ Length = PatchOffset + DBCS_MESSAGE_BUFFER_SIZE - base_offset;
+ }
+
+ // copy data from source to destination
+ memcpy( Target, Source, (WORD)Length );
+ }
+
+}
+
+
+BOOL RplReadData(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN DWORD read_offset,
+ OUT PDWORD pBytesRead
+ )
+/*++
+
+Routine Description:
+
+ Returns pointer to send buffer and its length. The data is read from
+ the offset (input param) of virtual boot block. The boot block consists
+ of small wksta specific data buffer, virtual file block and unused
+ memory between them (if any). Virtual file block is a file table.
+ The names are in memory, but the data is in hard disk (or in disk cache).
+
+Arguments:
+
+ read_offset - offset of data that we need to read
+ pBuffer - address of location where pointer to send buffer is returned
+ pBytesRead - pointer to number of bytes read
+
+Return Value:
+ TRUE if success, else FALSE.
+
+--*/
+{
+ DWORD bytes_read = 0; // the length of send buffer
+ LONG sint; // signed integer for length comp
+ DWORD temp;
+ DWORD read_len;
+ DWORD file_len;
+ PBYTE wksta_buf;
+ DWORD wksta_buf_len;
+ DWORD LengthDataBuffer;
+ PBYTE pDataBuffer;
+ PFLIST_TBL flist_tbl;
+ DWORD flist_tbl_len;
+ DWORD cur_flist_i;
+ DWORD cur_offset;
+ DWORD cur_file_base_offset;
+ DWORD fblock_base_offset;
+ BOOL is_end_of_bblock;
+ BOOL LAN_err;
+
+// RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplReadData"));
+
+ //
+ // The following is set in RplWorkerThread2
+ //
+ LengthDataBuffer = pWorkerData->send_buf_len;
+
+ //
+ // The following are set in RplMakeWkstaBuf
+ //
+ wksta_buf = pWorkerData->wksta_buf;
+ wksta_buf_len = pWorkerData->wksta_buf_len;
+ fblock_base_offset = pWorkerData->fblock_base_offset;
+
+ //
+ // The following are set in RplOpenData
+ //
+ pDataBuffer = pWorkerData->pDataBuffer;
+
+ flist_tbl = pWorkerData->flist_tbl;
+ flist_tbl_len = pWorkerData->flist_tbl_len;
+
+ cur_flist_i = pWorkerData->cur_flist_i;
+ cur_offset = pWorkerData->cur_offset;
+ cur_file_base_offset = pWorkerData->cur_file_base_offset;
+ is_end_of_bblock = pWorkerData->is_end_of_bblock;
+
+
+ if ( fblock_base_offset > read_offset) {
+
+ // We have to copy at least some data from wksta buffer or/and from
+ // the empty space between "wksta_buf_len" (end of wksta buffer
+ // data) and "fblock_base_offset" (beginning of file list data).
+ // First copy the data from wksta buffer then from empty space.
+
+ if ( wksta_buf_len > read_offset) { // wksta buffer data
+
+ if ( wksta_buf_len >= read_offset + LengthDataBuffer) {
+ bytes_read = LengthDataBuffer; // all is here
+ } else {
+ // Some but not all of the leftover data is here.
+ bytes_read = wksta_buf_len - read_offset;
+ }
+
+ memcpy( pDataBuffer, &wksta_buf[read_offset], bytes_read);
+ }
+
+ if ( bytes_read < LengthDataBuffer) { // empty space data
+
+ if ( read_offset + LengthDataBuffer <= fblock_base_offset) {
+ temp = LengthDataBuffer - bytes_read; // rest is here
+ } else {
+ // Some but not all of the leftover data is here.
+ temp = fblock_base_offset - wksta_buf_len;
+ }
+
+ memset( &pDataBuffer[ bytes_read], 0, temp);
+ bytes_read += temp;
+ }
+ }
+
+ //
+ // Check if the right file is opened and the seek is OK. There may
+ // have been a LAN I/O error in which case we must read the data from
+ // a new position.
+ //
+ if ( read_offset == cur_offset || cur_offset == -1L) {
+ LAN_err = FALSE; // everything OK
+
+ } else {
+ LAN_err = TRUE; // error in LAN I/O, this is not a sequential read
+ //
+ // this flag is set if we were reading the last buffer,
+ // reset flag, otherwise the rest is not read with ReadFile()
+ //
+ is_end_of_bblock = FALSE;
+ }
+
+
+ if ( bytes_read < LengthDataBuffer && !is_end_of_bblock) {
+ //
+ // All the data has not been read yet. Read the rest from
+ // files mentioned in the file list table.
+ //
+ if ( LAN_err && pWorkerData->hFile != INVALID_HANDLE_VALUE) {
+ if ( !CloseHandle( pWorkerData->hFile)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+ }
+
+ // Check if this read is from a wrong position or there was
+ // fatal error in the first file read => start from the beginning.
+
+ if ( LAN_err && read_offset > fblock_base_offset) {
+ //
+ // error => reset bytes_read counter, reject the possible data
+ // resend an old packet, search the new file of read_offset
+ //
+ bytes_read = 0;
+ cur_file_base_offset = fblock_base_offset;
+
+ for ( cur_flist_i = 0; cur_flist_i < flist_tbl_len; cur_flist_i++) {
+ if (read_offset <
+ cur_file_base_offset + flist_tbl[cur_flist_i].FileData.file_len) {
+
+ break; // found, exit the search loop
+ }
+ cur_file_base_offset += flist_tbl[cur_flist_i].FileData.file_len;
+ }
+
+ //
+ // If wksta has sent a wrong read offset, terminate read ahead.
+ //
+ if ( cur_flist_i >= flist_tbl_len) {
+ is_end_of_bblock = TRUE;
+ goto end_of_block_exit;
+ }
+
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+
+ //
+ // seek the current position, we are probably not reading from
+ // the start of file
+ //
+ file_len = SetFilePointer( pWorkerData->hFile,
+ read_offset - cur_file_base_offset, NULL, FILE_BEGIN);
+ if ( file_len == INVALID_FILE_OFFSET) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ } else {
+ //
+ // Open the primary file, if it's not opened yet
+ //
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ if (read_offset <= fblock_base_offset) {
+ // open the first file
+ cur_flist_i = 0;
+ cur_file_base_offset = fblock_base_offset;
+ }
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+ }
+ }
+
+ //
+ // read files until send buffer is full
+ //
+ while( bytes_read < LengthDataBuffer) {
+ if ( !ReadFile( pWorkerData->hFile, &pDataBuffer[bytes_read],
+ LengthDataBuffer - bytes_read, &read_len, NULL)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileRead;
+ return( FALSE);
+ }
+
+ bytes_read += read_len;
+
+ if (bytes_read == LengthDataBuffer) {
+ break; // we read all the data for now, exit the loop
+ }
+ //
+ // We have not read all the data we asked for. Therefore, we
+ // must have reached end of file. Therefore, close the current
+ // file handle and prepare to read the next file.
+ //
+ if ( !CloseHandle( pWorkerData->hFile)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+
+ pWorkerData->hFile = INVALID_HANDLE_VALUE;
+ cur_file_base_offset += flist_tbl[cur_flist_i].FileData.file_len;
+ cur_flist_i++;
+
+ // reset the last bytes in the file
+
+ temp = cur_file_base_offset - read_offset;
+
+ if ( (sint = (LONG)(temp - bytes_read)) > 0) {
+ memset( pDataBuffer + bytes_read, 0, sint );
+ }
+ bytes_read = temp;
+
+ if ( cur_flist_i >= flist_tbl_len) {
+ is_end_of_bblock = TRUE;
+ break; // end of file list
+ }
+
+ //
+ // Open file for reading
+ //
+ pWorkerData->hFile = CreateFile( flist_tbl[cur_flist_i].path_name,
+ GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0L);
+ if ( pWorkerData->hFile == INVALID_HANDLE_VALUE) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventStrings[ 1] = flist_tbl[cur_flist_i].path_name;
+ pWorkerData->EventId = NELOG_RplWkstaFileOpen;
+ return( FALSE);
+ }
+ }
+ }
+
+ cur_offset = read_offset + LengthDataBuffer;
+
+ //
+ // only for end of boot block
+ //
+
+end_of_block_exit:
+
+ //
+ // update the changed values
+ //
+
+ pWorkerData->cur_offset = cur_offset;
+ pWorkerData->cur_file_base_offset = cur_file_base_offset;
+ pWorkerData->cur_flist_i = cur_flist_i;
+ pWorkerData->is_end_of_bblock = is_end_of_bblock;
+
+ //
+ // Make NLS (DBCS ?) patch to RPLBOOT.SYS.
+ //
+
+ if ( pWorkerData->MakePatch == TRUE) {
+ RplMakePatch( pWorkerData, pDataBuffer, bytes_read, read_offset);
+ }
+
+ //
+ // return the buffer address and the length
+ //
+
+ *pBytesRead = bytes_read;
+// RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplReadData"));
+ return( TRUE);
+
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/read.h b/private/net/svcdlls/rpl/server/read.h
new file mode 100644
index 000000000..ee571320d
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/read.h
@@ -0,0 +1,29 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ read.h
+
+Abstract:
+
+ Exports from read.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplReadData(
+ IN PRPL_WORKER_DATA pWorkerData,
+ IN DWORD read_offset,
+ OUT PDWORD bytes_read_ptr
+ );
diff --git a/private/net/svcdlls/rpl/server/report.c b/private/net/svcdlls/rpl/server/report.c
new file mode 100644
index 000000000..7063e12ca
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/report.c
@@ -0,0 +1,199 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ report.c
+
+Abstract:
+
+ Prints messages to the error log of Lan Manager.
+
+ Provides similar functionality to rplermsg.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "report.h"
+
+VOID RplAlertRaise( IN DWORD ErrorCode);
+
+
+VOID RplEnd( IN DWORD ErrorCode)
+/*++
+
+Routine Description:
+
+ This function is called under very unusual circumstances!
+ It provides a convenient way for service to log an event, send
+ an alert, then exits.
+
+Arguments:
+ ErrorCode - termination error code
+
+Return Value:
+ None.
+
+--*/
+{
+ RplReportEvent( ErrorCode, NULL, 0, NULL );
+ RplAlertRaise( (RG_ServiceStatus.dwCurrentState == SERVICE_INSTALL_PENDING)
+ ? NERR_RplBootStartFailed : NERR_RplBootServiceTerm);
+ (VOID)RplServiceAttemptStop(); // signal service to stop
+}
+
+
+
+VOID RplAlertRaise( IN DWORD ErrorCode)
+/*++
+
+Routine Description:
+
+ Sends an ADMIN alert. The input is a LanManager error message.
+
+ This is a combination of the original Send_alert() routine &&
+ RaiseAlert() routine from logonsrv\server\error.c
+
+Arguments:
+ ErrorCode - the alert to be raised, text in alertmsg.h
+
+Return Value:
+ None.
+
+Notes:
+ Failing to post an alert is considered unimportant. This is why this
+ function is VOID.
+
+--*/
+{
+ char message[ ALERTSZ + sizeof(STD_ALERT) + sizeof(ADMIN_OTHER_INFO)];
+ PSTD_ALERT alert = (PSTD_ALERT)message;
+ PADMIN_OTHER_INFO other = (PADMIN_OTHER_INFO)ALERT_OTHER_INFO( alert);
+ LARGE_INTEGER time;
+ HANDLE fileHandle;
+ DWORD inBytes;
+ DWORD outBytes;
+
+ NtQuerySystemTime( &time);
+ RtlTimeToSecondsSince1970( &time, &alert->alrt_timestamp );
+
+ // Original code used alrt_servicename == SERVICE_SERVER
+ wcscpy( alert->alrt_servicename, SERVICE_RIPL);
+ wcscpy( alert->alrt_eventname, ALERT_ADMIN_EVENT );
+
+ other->alrtad_errcode = ErrorCode;
+ other->alrtad_numstrings = 0;
+
+
+ // NetAlertRaise() is gone, must use mailslots instead. So, first
+ // open the Alerter mailslot to write to it.
+
+ fileHandle = CreateFile(
+ ALERTER_MAILSLOT,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ (LPSECURITY_ATTRIBUTES) NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if ( fileHandle == INVALID_HANDLE_VALUE) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error opening alerter mailslot, error=%d",
+ GetLastError()));
+ return;
+ }
+
+ inBytes = min( sizeof( message),
+ (DWORD)( (PCHAR)ALERT_VAR_DATA(other) - (PCHAR)message));
+
+ // Write alert notification to mailslot to be read by Alerter service
+ if ( !WriteFile(
+ fileHandle,
+ message,
+ inBytes,
+ &outBytes,
+ NULL) || inBytes != outBytes) {
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error writing to alerter mailslot %d",
+ GetLastError()));
+
+ } else if ( ! CloseHandle( fileHandle)) {
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "AlertRaise: Error closing alerter mailslot %d",
+ GetLastError()
+ ));
+ }
+ (VOID)CloseHandle( fileHandle);
+}
+
+
+
+VOID RplReportEventEx(
+ IN DWORD MessageId,
+ IN LPWSTR * aStrings
+ )
+/*++
+
+Routine Description:
+
+ Writes an event in the event log.
+ A related function lives is RplReportEvent() in lib\report.c.
+ These two functions should be united.
+
+Arguments:
+
+ MessageId - Message ID
+ aStrings - a NULL terminated array of strings
+
+Return Value:
+
+ None.
+
+--*/
+{
+ WORD cStrings;
+ HANDLE logHandle;
+
+ logHandle = RegisterEventSource( NULL, RPL_EVENTLOG_NAME);
+
+ // If the event log cannot be opened, just return.
+
+ if ( logHandle == NULL) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ return;
+ }
+
+ for ( cStrings = 0; aStrings[ cStrings] != NULL; cStrings++) {
+ NOTHING;
+ }
+
+ if ( !ReportEvent(
+ logHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId, // event id
+ NULL, // user SID. We're local system - uninteresting
+ cStrings, // number of strings
+ 0, // raw data size
+ aStrings, // string array
+ NULL // raw data buffer
+ )) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ }
+
+ DeregisterEventSource( logHandle);
+}
+
diff --git a/private/net/svcdlls/rpl/server/report.h b/private/net/svcdlls/rpl/server/report.h
new file mode 100644
index 000000000..be3b621e1
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/report.h
@@ -0,0 +1,34 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ report.h
+
+Abstract:
+
+ Exports from report.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// VOID RplReportEvent( - declared in ..\inc\rpl.h
+//
+
+VOID RplEnd( IN DWORD ErrorCode);
+VOID RplReportEventEx(
+ IN DWORD MessageId,
+ IN LPWSTR * aStrings
+ );
+
diff --git a/private/net/svcdlls/rpl/server/request.c b/private/net/svcdlls/rpl/server/request.c
new file mode 100644
index 000000000..4a409dee2
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/request.c
@@ -0,0 +1,697 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ request.c
+
+Abstract:
+
+ Request thread code.
+
+ Provides similar functionality to rplreq.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "worker.h"
+#include "database.h"
+#include "report.h"
+#include "request.h"
+
+typedef struct _RPL_SFR_PARAMS { // send file request thread parameters
+ POPEN_INFO pOpenInfo; // open info data of current DLL
+ PPRCB HeadRcbList; // list of RCBs used by worker threads
+ PCRITICAL_SECTION ProtectRcbList;
+} SFR_PARAMS, *PRPL_SFR_PARAMS;
+
+
+extern VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+
+
+
+VOID SfrThread( PRPL_SFR_PARAMS pSfrParams)
+/*++
+
+Routine Description:
+
+ Procedure receives Send File Requests, searches the valid RCB
+ from list and starts the worker thread of RCB by clearing a
+ semaphore.
+
+ This thread tries to run at highest settable priority because
+ claims must always be ready to receive acknowledges (if ASCLAN
+ receive is not active, then SF Requests may be lost and there
+ is 4 seconds timeout in the workstation).
+
+Arguments:
+ pSfrParams ptr to SFR_PARAMS
+
+Return Value:
+ None.
+
+--*/
+{
+ DWORD status;
+
+ if ( !SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
+ status = GetLastError();
+ RplDump( ++RG_Assert, ("status=%d", status));
+ RplEnd( NELOG_RplSystem);
+ return;
+ }
+
+ //
+ // The call to SendFileRequest never returns. Not only is this thread
+ // capt captive, but the callee needlessly creates one more thread.
+ // Better interface would allow to post a receive for our two SAP-s,
+ // then from a single place read both FIND frame data (FIND_SAP)
+ // and SFR data (SFR_SAP). This could be all done from a single
+ // thread, resembling current RequestThread().
+ //
+
+ if ( RplDlcSfr( // was RPL1_Get_SF_Request()
+ pSfrParams->pOpenInfo,
+ pSfrParams->HeadRcbList,
+ pSfrParams->ProtectRcbList
+ )) {
+ return; // success
+ }
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ return; // somebody else initiated service shutdown
+ }
+ RplDump( ++RG_Assert, ("CurrentState=0x%x", RG_ServiceStatus.dwCurrentState));
+ RplEnd( NELOG_RplSystem);
+}
+
+
+
+PRCB RplPopRcb( PPRCB HeadList)
+{
+ PRCB pRcb = *HeadList;
+ DebugCheckList( HeadList, NULL, 0);
+ if ( pRcb != NULL) {
+ *HeadList = pRcb->next_rcb;
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "PopRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+ return( pRcb);
+}
+
+
+PRCB GetRcbFromFreeList(
+ IN OUT PPRCB pFreeList,
+ IN DWORD lan_rcb_size
+ )
+/*++
+
+Routine Description:
+
+ Finds the first RCB from the list of free RCBs. If list is empty,
+ creates a new RCB. Before returning RCB it (re)initializes all the
+ fields in the RCB.
+
+Arguments:
+
+ pFreeList, - pointer to list of free RCB-s
+ lan_rcb_size - size of lan specific rcb
+
+Return Value:
+
+ Pointer to RCB, or NULL if we cannot get an RCB.
+
+--*/
+{
+ PRCB pRcb;
+
+ EnterCriticalSection( &RG_ProtectRcbList);
+
+ pRcb = RplPopRcb( pFreeList);
+ if ( pRcb != NULL) {
+ goto exit_GetRcbFromFreeList; // success
+ }
+
+ pRcb = RplMemAlloc( RG_MemoryHandle, sizeof(RCB));
+ if ( pRcb == NULL) {
+ goto exit_GetRcbFromFreeList;
+ }
+
+ pRcb->lan_rcb_ptr = RplMemAlloc( RG_MemoryHandle, lan_rcb_size);
+ if ( pRcb->lan_rcb_ptr == NULL) {
+ RplMemFree( RG_MemoryHandle, pRcb);
+ pRcb = NULL;
+ goto exit_GetRcbFromFreeList; // failure
+ }
+
+ // SF_wakeup was using automatic reset and RPL worker used not to
+ // reset SF_wakup before each FDR send. This did not work properly
+ // due to multiple SFR-s for a single frame. Multiple SFR-s would
+ // cause SF_wakeup to be in a signalled state immediately after
+ // worker sent an FDR, thus worker would wakeup immediately and find
+ // a wrong value for sfr_seq_number.
+ // To fix this RPL worker must make sure SF_wakeup is blocked
+ // before each FDR send. Since worker must now reset SF_wakeup
+ // anyway, SF_wakeup is now using manual reset.
+ //
+ pRcb->SF_wakeup = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ TRUE, // initial value is signalled
+ NULL // event does not have a name
+ );
+ if ( pRcb->SF_wakeup == NULL) {
+ RplDump( ++RG_Assert,("CreateEvent => error=%d", GetLastError()));
+ RplMemFree( RG_MemoryHandle, pRcb->lan_rcb_ptr);
+ RplMemFree( RG_MemoryHandle, pRcb);
+ pRcb = NULL;
+ goto exit_GetRcbFromFreeList; // failure
+ }
+
+exit_GetRcbFromFreeList:
+
+ pRcb->AdapterName[ NODE_ADDRESS_LENGTH] = 0;
+ *pRcb->volume_id = 0;
+ pRcb->sfr_seq_number = 0;
+ pRcb->fdr_seq_number = 0;
+ pRcb->ReceivedSfr = FALSE;
+
+ *(PBYTE)&pRcb->flags = 0; // reset the error flags
+ pRcb->next_rcb = NULL;
+ *(PBYTE)&pRcb->flags = 0;
+
+ pRcb->Pending = TRUE;
+ pRcb->SFR = FALSE;
+
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "GetRcbFromFreeList: pRcb=0x%x", pRcb));
+ return( pRcb);
+}
+
+
+PRPL_WORKER_PARAMS GetWorkerParams(
+ IN PRPL_REQUEST_PARAMS pRequestParams,
+ OUT PBOOL pShutdown
+ )
+/*++
+ Loops until number of worker threads falls below the maximum value
+ allowed, or until it gets signalled to die.
+ If it is signalled to die, waits for all worker children and returns.
+ If it is not signalled to die, waits only for children that are
+ already exiting, and returns pointer to one of available RPL_WORKER_PARAMS
+ structures.
+--*/
+{
+ HANDLE eventArray[ 2];
+ PRPL_WORKER_PARAMS pWorkerParams;
+ PRPL_WORKER_PARAMS pFoundWorkerParams;
+ DWORD status;
+ DWORD WorkerCount;
+
+ eventArray[ 0] = RG_EventWorkerCount;
+ eventArray[ 1] = RG_TerminateNowEvent;
+
+ *pShutdown = FALSE;
+
+ for ( ; ;) {
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ WorkerCount = RG_WorkerCount;
+ if ( WorkerCount < RG_MaxWorkerCount) {
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+ break;
+ }
+ //
+ // We reset event from within critical section to make sure
+ // event is never reset for wrong reasons.
+ //
+ if ( !ResetEvent( RG_EventWorkerCount)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+ status = WaitForMultipleObjects(
+ 2, // count
+ eventArray, // handles
+ FALSE, // wait for at least one
+ INFINITE // wait for ever
+ );
+ if ( status == 1) {
+ *pShutdown = TRUE; // terminate now event
+ break; // to wait for children below
+ }
+ RPL_ASSERT( status == 0); // WorkerCount changed
+ continue;
+ }
+
+ //
+ // If we are shutting down, wait on all valid thread handles.
+ // Else, wait only on thread handles for children that are exiting.
+ //
+ for ( pFoundWorkerParams = NULL,
+ pWorkerParams = pRequestParams->pWorkerParams;
+ pWorkerParams != NULL;
+ pWorkerParams = pWorkerParams->pWorkerParams) {
+ if ( pWorkerParams->Exiting == TRUE ||
+ (*pShutdown == TRUE && pWorkerParams->ThreadHandle != NULL)) {
+ status = WaitForSingleObject( pWorkerParams->ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "pWorkerParams=0x%x, status=0x%x",
+ pWorkerParams, status==WAIT_FAILED ? GetLastError() : status));
+ }
+ if ( !CloseHandle( pWorkerParams->ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "pWorkerParams=0x%x, error=%d",
+ pWorkerParams, GetLastError()));
+ }
+ pWorkerParams->ThreadHandle = NULL; // OK to reuse
+ pWorkerParams->Exiting = FALSE;
+ }
+ if ( pWorkerParams->ThreadHandle == NULL) {
+ pFoundWorkerParams = pWorkerParams; // reuse this structure
+ }
+ }
+ if ( *pShutdown == TRUE) {
+ return( NULL);
+ }
+ if ( pFoundWorkerParams == NULL) {
+ //
+ // Allocate a brand new RPL_WORKER_PARAMS structure.
+ //
+ pFoundWorkerParams = RplMemAlloc( RG_MemoryHandle, sizeof(RPL_WORKER_PARAMS));
+ if ( pFoundWorkerParams == NULL) {
+ RplDump( ++RG_Assert, ( ""));
+ return( NULL);
+ }
+ pFoundWorkerParams->pWorkerParams = pRequestParams->pWorkerParams;
+ pRequestParams->pWorkerParams = pFoundWorkerParams;
+ pFoundWorkerParams->ThreadHandle = NULL;
+ pFoundWorkerParams->ThreadId = 0;
+ pFoundWorkerParams->pRequestParams = pRequestParams;
+ pFoundWorkerParams->pRcb = NULL;
+ pFoundWorkerParams->Exiting = FALSE;
+ }
+ return( pFoundWorkerParams);
+}
+
+
+
+VOID RequestThread( PRPL_REQUEST_PARAMS pRequestParams)
+/*++
+
+Routine Description:
+
+ Module is the RPL request thread. It must be re-entrant, because each
+ DLL has own instance of this module. Module loads rpl DLLs and gets
+ their entry point addresses. It initializes DLLs and dynamic memory
+ and starts RpldFind() loop.
+
+Arguments:
+
+ pRequestParams - string containing names of RPL1 & RPL2 DLLs
+
+Return Value:
+
+ None.
+
+Notes:
+
+ This routine must have a single exit point because it is essential
+ to decrement the number of request threads and wake up the main
+ thread on our way out.
+
+--*/
+{
+ PRPL_SFR_PARAMS pSfrParams;
+ DWORD ThreadId;
+ DWORD status;
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRCB pRcb; // current Resource Control Block
+ PRPL_WORKER_PARAMS pWorkerParams;
+ BOOL Shutdown;
+ HANDLE ThreadHandle;
+ //
+ // TRUE if we inserted RCB in pending state on a worker queue.
+ // Used only when "pRcb" is not NULL i.e. while it still makes
+ // sense keeping pointer to that RCB.
+ //
+ BOOL RcbOnWorkerList;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RequestThread(0x%x)", pRequestParams));
+
+ ThreadHandle = NULL;
+ pOpenInfo = pRequestParams->pOpenInfo;
+
+ //
+ // Build parameter block for SfrThread
+ //
+ pSfrParams = RplMemAlloc( RG_MemoryHandle, sizeof( SFR_PARAMS));
+ if( pSfrParams == NULL) {
+ status = GetLastError();
+ RplDump( ++RG_Assert, ("status=%d", status));
+ RplEnd( ERROR_NOT_ENOUGH_MEMORY);
+ goto exit_RequestThread;
+ }
+ pSfrParams->HeadRcbList = &pRequestParams->BusyRcbList;
+ pSfrParams->ProtectRcbList = &RG_ProtectRcbList;
+ pSfrParams->pOpenInfo = pOpenInfo;
+
+ //
+ // Create Get_SF_Request thread, it receives Send File Requests
+ // from workstations, searches rcb from list and clears its semaphore,
+ // that releases the worker thread.
+ //
+ ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)SfrThread, pSfrParams, 0, &ThreadId);
+ if ( ThreadHandle == NULL) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ RplEnd( ERROR_NOT_ENOUGH_MEMORY);
+ goto exit_RequestThread;
+ }
+
+ //
+ // Read RPL request packets forever. Never create more than the
+ // maximum allowed number of worker threads.
+ //
+ for ( pRcb = NULL, pWorkerParams = NULL; NOTHING; NOTHING) {
+ //
+ // If pRcb is NULL we need to get an RCB from the free list.
+ // If pRcb is not NULL then we can still use it but we must
+ // dequeue it from worker queue if we placed it there.
+ //
+ if ( pRcb == NULL) {
+ for ( ; ;) {
+ pRcb = GetRcbFromFreeList(
+ &pRequestParams->FreeRcbList,
+ pOpenInfo->lan_rcb_size
+ );
+ if ( pRcb != NULL) {
+ break;
+ }
+ Sleep( 1000L); // wait for things to recover
+ }
+ } else if ( RcbOnWorkerList == TRUE) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ }
+ RcbOnWorkerList = FALSE;
+
+ //
+ // Get the next RPL request. In the current design here we can
+ // block for ever if there are no FIND frames arriving. The only
+ // way then to let this thread out is for the main thread to call
+ // RPL_Term(). This would then cause RplDlcFind() to
+ // return with error, and we would exit main loop.
+ // A better interface would provide asynchronous calls, just the
+ // way DLC does. Then, ideally we would wait here for multiple
+ // events such as:
+ // - FIND frame arrival event
+ // - TerminateNowEvent
+ //
+
+ if ( !RplDlcFind( pOpenInfo, pRcb)) {
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ goto exit_RequestThread;
+ }
+ RplDump( ++RG_Assert, ("CurrentState=0x%x", RG_ServiceStatus.dwCurrentState));
+ continue;
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "Request(0x%x): RplDlcFind => pRcb=0x%x, AdapterName=%ws",
+ pRequestParams, pRcb, pRcb->AdapterName));
+
+ //
+ // Send buffers must be paragraph aligned because of Jump address
+ // patching to the rplboot.sys code (dword fits always to one
+ // boot block)
+ //
+ pRcb->max_frame &= 0xFFF0;
+
+ //
+ // "max_frame" is sent to us by the client as the max value of data
+ // that the client can handle. We can go smaller than that but never
+ // larger.
+ //
+ if (pRcb->max_frame > MAX_FRAME_SIZE) {
+ pRcb->max_frame = MAX_FRAME_SIZE;
+ }
+
+ //
+ // Service this client if it is not already serviced by some of our
+ // children, and we have enough threads and we can find a matching
+ // wksta record.
+ //
+
+ EnterCriticalSection( &RG_ProtectRcbList);
+
+ if ( RplFindRcb( &pRequestParams->BusyRcbList, pRcb->NodeAddress) != NULL) {
+ //
+ // Client is already serviced by some of our children.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "Request(0x%x): pRcb=0x%x, AdapterName=%ws client is already serviced",
+ pRequestParams, pRcb, pRcb->AdapterName));
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ continue;
+ }
+
+ RcbOnWorkerList = TRUE; // to get it back in case of errors
+ pRcb->Pending = TRUE; // so SFR thread does not get confused
+ RplInsertRcb( &pRequestParams->BusyRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+
+ if ( !RplRequestHaveWksta( pRcb->AdapterName)) {
+ continue; // no wksta record for this AdapterName
+ }
+
+ if ( pWorkerParams == NULL) {
+ pWorkerParams = GetWorkerParams( pRequestParams, &Shutdown);
+ if ( Shutdown == TRUE) {
+ goto exit_RequestThread;
+ } else if ( pWorkerParams == NULL) {
+ continue;
+ }
+ }
+
+ pWorkerParams->pRcb = pRcb;
+ pWorkerParams->ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)RplWorkerThread, pWorkerParams, 0,
+ &pWorkerParams->ThreadId);
+ if ( pWorkerParams->ThreadHandle == NULL) {
+ RplDump( ++RG_Assert,( "Error=%d", GetLastError()));
+ RplReportEvent( ERROR_NOT_ENOUGH_MEMORY, NULL, 0, NULL); // keep going
+ continue;
+ }
+ pRcb = NULL; // need to get a new RCB
+ pWorkerParams = NULL; // need to get a new WORKER_PARAMS
+ }
+
+exit_RequestThread:
+
+ if ( RcbOnWorkerList == TRUE) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ RplInsertRcb( &pRequestParams->FreeRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_REQUEST,(
+ "RequestThread(0x%x): on its way to exit", pRequestParams));
+
+ //
+ // Wait for SFR thread to exit then close its thread handle.
+ //
+ status = WaitForSingleObject( ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "Params=0x%x Handle=0x%x Id=0x%x status=0x%x",
+ pSfrParams, ThreadHandle, ThreadId, status));
+ }
+ if ( !CloseHandle( ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "Params=0x%x Handle=0x%x Id=0x%x error=%d",
+ pSfrParams, ThreadHandle, ThreadId, GetLastError()));
+ }
+ pRequestParams->Exiting = TRUE;
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RequestThread(0x%x)", pRequestParams));
+}
+
+
+BOOL AddToTermList( IN POPEN_INFO pOpenInfo)
+/*++
+
+Routine Description:
+
+Arguments:
+ pOpenInfo - info ptr for dll
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ PTERM_LIST pTermList;
+
+ pTermList = RplMemAlloc( RG_MemoryHandle, sizeof(TERM_LIST));
+ if ( pTermList == NULL) {
+ RG_Error = GetLastError();
+ return( FALSE);
+ }
+ pTermList->pOpenInfo = pOpenInfo;
+
+ EnterCriticalSection( &RG_ProtectTerminationList);
+ pTermList->next = RG_TerminationListBase;
+ RG_TerminationListBase = pTermList;
+
+ LeaveCriticalSection( &RG_ProtectTerminationList);
+ return( TRUE);
+}
+
+
+BOOL RplStartAdapters( VOID)
+/*++
+
+Routine Description:
+ Attempt to start all adapters.
+
+Return Value:
+ TRUE if at least one of the adapters have been started.
+ FALSE otherwise
+
+--*/
+{
+ POPEN_INFO pOpenInfo; // generic rpl DLL info
+ PRPL_REQUEST_PARAMS pRequestParams;
+ DWORD startedAdapters;
+ DWORD AdapterNumber;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplStartAdapters"));
+
+ pOpenInfo = NULL; // must be reinitialized every time
+ pRequestParams = NULL; // must be reinitialized every time
+
+ for ( AdapterNumber = 0, startedAdapters = 0;
+ AdapterNumber < MAX_ADAPTERS; AdapterNumber++) {
+
+ if ( pOpenInfo == NULL) {
+ pOpenInfo = RplMemAlloc( RG_MemoryHandle, sizeof(OPEN_INFO));
+ if ( pOpenInfo == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ memset( pOpenInfo, 0, sizeof( *pOpenInfo));
+ }
+ if ( pOpenInfo->adapt_info_ptr == NULL) {
+ pOpenInfo->adapt_info_size = MAX_ADAPTER_INFO_SIZE;
+ pOpenInfo->adapt_info_ptr = RplMemAlloc( RG_MemoryHandle, MAX_ADAPTER_INFO_SIZE);
+ if ( pOpenInfo->adapt_info_ptr == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ memset( pOpenInfo->adapt_info_ptr, 0, MAX_ADAPTER_INFO_SIZE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // just ignore this error
+ }
+#endif
+
+ //
+ // We try to start all adapters.
+ //
+ if ( !RplDlcInit( (POPEN_INFO)pOpenInfo, AdapterNumber)) {
+ continue; // try all adapters
+ }
+
+ //
+ // Save termination addresses of dll-s for usage by RplTerminateDlls().
+ //
+ if ( !AddToTermList( pOpenInfo)) {
+ break;
+ }
+
+ //
+ // Allocate & initialize request thread parameters.
+ //
+ if ( pRequestParams == NULL) {
+ pRequestParams = RplMemAlloc( RG_MemoryHandle, sizeof(RPL_REQUEST_PARAMS));
+ if ( pRequestParams == NULL) {
+ RG_Error = GetLastError();
+ break;
+ }
+ memset( pRequestParams, 0, sizeof( *pRequestParams));
+ pRequestParams->pOpenInfo = pOpenInfo;
+ pRequestParams->pRequestParams = RG_pRequestParams;
+ RG_pRequestParams = pRequestParams;
+
+ }
+ pRequestParams->ThreadHandle = CreateThread( NULL, 0,
+ (LPTHREAD_START_ROUTINE)RequestThread, pRequestParams, 0,
+ &pRequestParams->ThreadId);
+ if ( pRequestParams->ThreadHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ continue;
+ }
+ startedAdapters++;
+ pOpenInfo = NULL; // must be reinitialized every time
+ pRequestParams = NULL; // need to get new REQUEST_PARAMS
+ }
+
+ if ( pOpenInfo) {
+ if ( pOpenInfo->adapt_info_ptr) {
+ RplMemFree( RG_MemoryHandle, pOpenInfo->adapt_info_ptr);
+ }
+ RplMemFree( RG_MemoryHandle, pOpenInfo);
+ }
+
+ if ( startedAdapters == 0) {
+ RG_Error = RG_Error == NO_ERROR ? NERR_RplNoAdaptersStarted : RG_Error;
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplStartAdapters"));
+ return( TRUE);
+}
+
+
+VOID RplCloseAdapters( VOID)
+/*++
+
+Routine Description:
+
+ Terminates all the DLL-s in RPL termination list.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PTERM_LIST pTerm;
+
+ for ( pTerm = RG_TerminationListBase; pTerm != NULL; pTerm = pTerm->next) {
+ RplDlcTerm( pTerm->pOpenInfo);
+ }
+}
+
diff --git a/private/net/svcdlls/rpl/server/request.h b/private/net/svcdlls/rpl/server/request.h
new file mode 100644
index 000000000..68e847db8
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/request.h
@@ -0,0 +1,26 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ request.h
+
+Abstract:
+
+ Exports from request.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+BOOL RplStartAdapters( VOID);
+VOID RplCloseAdapters( VOID);
diff --git a/private/net/svcdlls/rpl/server/resume.c b/private/net/svcdlls/rpl/server/resume.c
new file mode 100644
index 000000000..e2175a179
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/resume.c
@@ -0,0 +1,308 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ resume.c
+
+Abstract:
+
+ Module for dealing with resume keys used in enumerations.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "config.h"
+#include "database.h"
+#define RPLRESUME_ALLOCATE
+#include "resume.h"
+#undef RPLRESUME_ALLOCATE
+
+#ifdef RPL_DEBUG
+
+VOID ResumeList(
+ IN PRPL_SESSION pSession,
+ IN PCHAR String
+ )
+{
+ WCHAR ResumeValue[ 20];
+ DWORD NameSize;
+ JET_ERR ForJetError;
+ DWORD ResumeHandle;
+ DWORD ServerHandle;
+
+ Call( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+
+ //
+ // We could skip JetMove, but if we do so, we need to modify error
+ // testing for the first JetRetrieveColumn() call (it would return
+ // JET_errNoCurrentRecord for the empty table).
+ //
+
+ for ( ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveFirst, 0);
+ ForJetError == JET_errSuccess;
+ ForJetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0)) {
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeHandle].ColumnId,
+ &ResumeHandle, sizeof( ResumeHandle), &NameSize, 0, NULL));
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId,
+ ResumeValue, sizeof( ResumeValue), &NameSize, 0, NULL));
+ Call( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ServerHandle].ColumnId,
+ &ServerHandle, sizeof( ServerHandle), &NameSize, 0, NULL));
+ RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, (
+ "%s ResumeH=0x%x,Value=%ws,ServerH=0x%x",
+ String, ResumeHandle, ResumeValue, ServerHandle));
+ }
+}
+#endif // RPL_DEBUG
+
+
+DWORD ResumeCreateTable( OUT PRPL_SESSION pSession)
+/*++
+ Table gets created, then closed. The reason we do not keep the table
+ open is that creator of a table holds exclusive access to that table
+ until he/she closes the table. This would prevent other threads from
+ using the table.
+--*/
+{
+ JET_COLUMNDEF ColumnDef;
+ JET_ERR JetError;
+ DWORD index;
+ DWORD Offset;
+ CHAR IndexKey[ 255];
+
+ JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
+ RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
+ if ( JetError == JET_errTableDuplicate) {
+ //
+ // This would happend only if last time we did not shutdown properly
+ // so ResumeTable never got deleted.
+ //
+ CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ JetError = JetCreateTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME,
+ RESUME_TABLE_PAGE_COUNT, RESUME_TABLE_DENSITY, &pSession->ResumeTableId);
+ }
+ if ( JetError != JET_errSuccess) {
+ return( MapJetError( JetError));
+ }
+
+ //
+ // Create columns. First initalize fields that do not change between
+ // addition of columns.
+ //
+ ColumnDef.cbStruct = sizeof(ColumnDef);
+ ColumnDef.columnid = 0;
+ ColumnDef.wCountry = 1;
+ ColumnDef.langid = 0x0409; // USA english
+ ColumnDef.cp = 1200; // UNICODE codepage
+ ColumnDef.wCollate = 0;
+ ColumnDef.cbMax = 0;
+
+ for ( index = 0; index < RESUME_TABLE_LENGTH; index++) {
+
+ ColumnDef.coltyp = ResumeTable[ index].ColumnType;
+ //
+ // All columns are variable length, but resume handle is autoincrement
+ // as well.
+ //
+ ColumnDef.grbit = (index==RESUME_ResumeHandle) ?
+ JET_bitColumnAutoincrement : 0;
+
+ CallM( JetAddColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ index].ColumnName, &ColumnDef,
+ NULL, 0, &ResumeTable[ index].ColumnId));
+ }
+
+ //
+ // We need resume handle as an index, so that at Enum() time we quickly
+ // find resume value for a given resume handle. Beging primary index,
+ // this index is also unique.
+ //
+ Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ResumeHandle].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle,
+ JET_bitIndexPrimary, IndexKey, Offset, 50));
+
+ //
+ // We need server handle as an index, so that at Close() time we quickly
+ // enumerate all resume keys for a given server handle, then delete them.
+ // This index is NOT unique since we can have a number of resume handles
+ // per client.
+ //
+ Offset = AddKey( IndexKey, '+', ResumeTable[ RESUME_ServerHandle].ColumnName);
+ IndexKey[ Offset++] = '\0';
+ CallM( JetCreateIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle,
+ 0, IndexKey, Offset, 50));
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "ResumeCreateTable");
+ }
+#endif // RPL_DEBUG
+
+ CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ return( ERROR_SUCCESS);
+}
+
+
+
+DWORD ResumeDeleteTable(
+ IN PRPL_SESSION pSession
+ )
+{
+ if ( pSession->ResumeTableId != 0) {
+ CallM( JetCloseTable( pSession->SesId, pSession->ResumeTableId));
+ CallM( JetDeleteTable( pSession->SesId, pSession->DbId, RESUME_TABLE_NAME));
+ }
+ return( NO_ERROR);
+}
+
+
+BOOL ResumeKeyGet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ResumeHandle,
+ OUT PVOID ResumeValue,
+ IN OUT PDWORD pResumeSize
+ )
+{
+ DWORD DataSize;
+#ifdef RPL_DEBUG_NEVER
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumeKeyGet");
+ }
+#endif // RPL_DEBUG_NEVER
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+ CallB( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ResumeHandle,
+ sizeof( ResumeHandle), JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue,
+ *pResumeSize, &DataSize, 0, NULL));
+ if ( DataSize > *pResumeSize) {
+ RplDump( ++RG_Assert, ( "DataSize=%d", DataSize));
+ return( FALSE);
+ }
+ *pResumeSize = DataSize;
+ return( TRUE);
+}
+
+
+BOOL ResumeKeySet(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PVOID ResumeValue,
+ IN DWORD ResumeSize,
+ OUT PDWORD pResumeHandle
+ )
+{
+ DWORD DataSize;
+
+ *pResumeHandle = 0; // just in case we fail below
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumeKeySet");
+ }
+#endif // RPL_DEBUG
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ResumeHandle));
+#if 0
+ //
+ // We do NOT call JetMove() here, because in the case of an empty table
+ // it returns error JET_errNoCurrentRecord. Also, if JetMove() is used
+ // here, then ordering of elements in the table is such that ResumePrune()
+ // function deletes only the OLDEST record for a given server handle.
+ //
+ CallB( JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveLast, 0));
+#endif
+ CallB( JetPrepareUpdate( pSession->SesId, pSession->ResumeTableId, JET_prepInsert));
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeHandle].ColumnId, pResumeHandle,
+ sizeof( *pResumeHandle), &DataSize, JET_bitRetrieveCopy, NULL));
+ if ( DataSize != sizeof( *pResumeHandle) || *pResumeHandle == 0) {
+ RplDump( ++RG_Assert, ( "DataSize=%d", DataSize));
+ return( FALSE);
+ }
+ CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ResumeValue].ColumnId, ResumeValue,
+ ResumeSize, 0, NULL));
+ CallB( JetSetColumn( pSession->SesId, pSession->ResumeTableId,
+ ResumeTable[ RESUME_ServerHandle].ColumnId, &ServerHandle,
+ sizeof( ServerHandle), 0, NULL));
+ CallB( JetUpdate( pSession->SesId, pSession->ResumeTableId, NULL, 0, NULL));
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel & RPL_DEBUG_RESUME) {
+ WCHAR ValueBuffer[ 32];
+ DWORD Length;
+ PWCHAR Value;
+ Length = wcslen( (PWCHAR)ResumeValue);
+ if ( (Length + 1) * sizeof(WCHAR) < ResumeSize) {
+ wcscpy( ValueBuffer, (PWCHAR)ResumeValue);
+ wcscat( ValueBuffer, L"!");
+ wcscat( ValueBuffer, (PWCHAR)ResumeValue + Length + 1);
+ Value = ValueBuffer;
+ } else {
+ Value = (PWCHAR)ResumeValue;
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_RESUME, (
+ "ResumeKeySet: Handle=0x%x, Value=%.20ws", *pResumeHandle, Value));
+ }
+#endif // RPL_DEBUG
+ return( TRUE);
+}
+
+
+VOID ResumePrune(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle
+ )
+/*++
+ This function returns no errors, since failure to delete old resume handles
+ is not considered fatal (and there is very little we can do about this).
+--*/
+{
+ JET_ERR JetError;
+
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "++ResumePrune");
+ }
+#endif // RPL_DEBUG
+ CallR( JetSetCurrentIndex( pSession->SesId, pSession->ResumeTableId, RESUME_INDEX_ServerHandle));
+ CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle,
+ sizeof( ServerHandle), JET_bitNewKey));
+#ifdef NOT_YET
+ JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ);
+#else
+ JetError = JetSeek( pSession->SesId, pSession->ResumeTableId, JET_bitSeekEQ | JET_bitSetIndexRange);
+#endif
+ if ( JetError == JET_errSuccess) {
+#ifdef NOT_YET
+ CallR( JetMakeKey( pSession->SesId, pSession->ResumeTableId, &ServerHandle,
+ sizeof( ServerHandle), JET_bitNewKey));
+ CallR( JetSetIndexRange( pSession->SesId, pSession->ResumeTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit));
+#endif
+ do {
+ Call( JetDelete( pSession->SesId, pSession->ResumeTableId));
+ JetError = JetMove( pSession->SesId, pSession->ResumeTableId, JET_MoveNext, 0);
+ } while ( JetError == JET_errSuccess);
+ }
+#ifdef RPL_DEBUG
+ if ( RG_DebugLevel == (DWORD)(-1)) {
+ ResumeList( pSession, "--ResumePrune");
+ }
+#endif // RPL_DEBUG
+}
+
diff --git a/private/net/svcdlls/rpl/server/resume.h b/private/net/svcdlls/rpl/server/resume.h
new file mode 100644
index 000000000..12c791d4a
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/resume.h
@@ -0,0 +1,61 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ resume.h
+
+Abstract:
+
+ Describes layout of JET database table used for RESUME structures.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#ifdef RPLRESUME_ALLOCATE
+#define EXTERN_RESUME
+#define INIT_RESUME( _x) = _x
+#else
+#define EXTERN_RESUME extern
+#define INIT_RESUME( _x)
+#endif
+
+//
+// Indices of entries in ResumeTable[] - resume key array.
+//
+#define RESUME_ResumeHandle 0
+#define RESUME_ResumeType 1
+#define RESUME_ResumeValue 2
+#define RESUME_ServerHandle 3
+#define RESUME_TABLE_LENGTH 4
+
+RPL_COLUMN_INFO ResumeTable[]
+#ifdef RPLRESUME_ALLOCATE
+ = {
+ { "ResumeHandle", JET_coltypLong, 0}, // for enumeration
+ { "ResumeType", JET_coltypLong, 0}, // type of operation
+ { "ResumeValue", JET_coltypBinary, 0}, // where to restart
+ { "ServerHandle", JET_coltypLong, 0}
+}
+#endif // RPLRESUME_ALLOCATE
+;
+
+#define RESUME_INDEX_ResumeHandle "foo" // + ResumeHandle
+#define RESUME_INDEX_ServerHandle "goo" // + ServerHandle
+
+#define RESUME_TABLE_NAME "Resume"
+#define RESUME_TABLE_PAGE_COUNT 5 // initial number of 4K pages
+#define RESUME_TABLE_DENSITY 100 // initial density
+
+EXTERN_RESUME JET_TABLEID ResumeTableId;
+
diff --git a/private/net/svcdlls/rpl/server/rpldata.h b/private/net/svcdlls/rpl/server/rpldata.h
new file mode 100644
index 000000000..26447bf68
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rpldata.h
@@ -0,0 +1,161 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rpldata.h
+
+Abstract:
+
+ Remote initial Program Load service global variables (external &
+ definitions).
+
+ Provides similar functionality to rplglob.c & rmapext.h in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+//
+// rplmain.c will #include this file with RPLDATA_ALLOCATE defined.
+// That will cause each of these variables to be allocated.
+//
+#ifdef RPLDATA_ALLOCATE
+#define EXTERN
+#define INIT( _x) = _x
+#else
+#define EXTERN extern
+#define INIT( _x)
+#endif
+
+#ifdef RPL_DEBUG
+EXTERN DWORD RG_Debug;
+EXTERN DWORD RG_BootCount;
+#endif // RPL_DEBUG
+
+
+EXTERN DWORD RG_Error;
+EXTERN DWORD RG_Null INIT( 0); // used in bbcfile for empty entries in string tables
+
+//
+// Synchronization Object for RPL server code
+//
+
+EXTERN CRITICAL_SECTION RG_ProtectRcbList;
+EXTERN CRITICAL_SECTION RG_ProtectServerHandle;
+EXTERN CRITICAL_SECTION RG_ProtectCurrentStatus; // service status
+
+
+EXTERN DWORD RG_ServerHandle; // service handle returned to client
+EXTERN DWORD RG_AdapterPolicy; // whether to log AdapterName-s of unknown clients
+EXTERN DWORD RG_BackupInterval; // how often to do database backup (in hours)
+EXTERN RPL_SESSION RG_RequestSession; // jet handles for request threads
+EXTERN RPL_SESSION RG_WorkerSession; // jet handles for worker threads
+EXTERN RPL_SESSION RG_ApiSession; // jet handles for api threads
+EXTERN BOOL RG_DetachDatabase; // do we need to detach jet database
+EXTERN PCHAR RG_Mdb; // points to rplsvc.mdb
+EXTERN PCHAR RG_BackupPath; // points to database backup path
+EXTERN JET_INSTANCE RG_Instance;
+EXTERN BOOL RG_InstanceAllocated;
+EXTERN CRITICAL_SECTION RG_ProtectRequestSession;
+EXTERN CRITICAL_SECTION RG_ProtectWorkerSession;
+EXTERN CRITICAL_SECTION RG_ProtectDatabase; // used for ApiSession also
+
+
+EXTERN HANDLE RG_TerminateNowEvent;
+
+//
+// Other globals of the actual ripl server code:
+//
+
+EXTERN SERVICE_STATUS RG_ServiceStatus;
+EXTERN SERVICE_STATUS_HANDLE RG_ServiceStatusHandle;
+EXTERN SC_HANDLE RG_ServiceHandle;
+EXTERN DWORD RG_Tasks;
+EXTERN CRITICAL_SECTION RG_ProtectServiceStatus;
+
+EXTERN HANDLE RG_MemoryHandle; // for memory "owned" by main thread
+EXTERN HANDLE RG_MessageHandle;
+
+EXTERN PRPL_REQUEST_PARAMS RG_pRequestParams;
+EXTERN CRITICAL_SECTION RG_ProtectRequestList;
+
+
+EXTERN DWORD RG_MaxWorkerCount; // upper limit on number of worker threads
+EXTERN DWORD RG_WorkerCount; // number of worker threads
+EXTERN HANDLE RG_EventWorkerCount; // event for changes in the above
+EXTERN CRITICAL_SECTION RG_ProtectWorkerCount;
+
+EXTERN CRITICAL_SECTION RG_ProtectTerminationList;
+EXTERN PTERM_LIST RG_TerminationListBase; // base of termination list
+
+//
+// The following globals are defined together with their default values.
+// These values may be overriden by user in lanman.ini & command line.
+//
+
+EXTERN WCHAR RG_Directory[ MAX_PATH]; // path to rpl service disk data
+EXTERN DWORD RG_DirectoryLength; // length of the above path
+
+EXTERN BOOL RG_ReadChecksum; // status set while files chksmmed
+EXTERN PBYTE RG_CodePageBuffer; // "pCodePage"
+EXTERN DWORD RG_CodePageSize; // "cbCodePage"
+
+//
+// This value contains the current adapter policy and also the startup flags
+//
+
+EXTERN DWORD RG_AdapterPolicy;
+
+//
+// If we ever separate DLC-RPL server from FILE-RPL server, then
+// RG_ComputerName should be a pointer to FILE-RPL server name.
+//
+EXTERN WCHAR RG_ComputerName[ MAX_COMPUTERNAME_LENGTH + 1];
+
+EXTERN PWCHAR RG_UncRplfiles; // UNC name sent to rpl client
+EXTERN PWCHAR RG_DiskRplfiles; // local path used in disk operations
+
+EXTERN PWCHAR RG_DiskBinfiles; // local path to binfiles root
+
+
+//
+// The following group of globals should be constant and they are
+// initialized only once, at load exe time (not every time at
+// start service time).
+//
+
+//
+// NLS patches of RPLBOOT.SYS and DLCLOADR.COM
+//
+
+EXTERN DWORD RG_MessageTable[]
+#ifdef RPLDATA_ALLOCATE
+ = {
+ NERR_BadDosRetCode,
+ NERR_ProgNeedsExtraMem,
+ NERR_BadDosFunction,
+ NERR_RemoteBootFailed,
+ NERR_BadFileCheckSum,
+ NERR_NoRplBootSystem
+}
+#endif // RPLDATA_ALLOCATE
+;
+
+#define MESSAGE_TABLE_LENGTH 6 // number of entries in the array above
+#define DBCS_MESSAGE_BUFFER_SIZE (MESSAGE_TABLE_LENGTH * DBCS_SINGLE_MESSAGE_BUFFER_SIZE)
+
+EXTERN PBYTE RG_DbcsMessageBuffer INIT( NULL);
+
+#undef EXTERN
+
+
diff --git a/private/net/svcdlls/rpl/server/rplmain.c b/private/net/svcdlls/rpl/server/rplmain.c
new file mode 100644
index 000000000..2d8c568e3
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplmain.c
@@ -0,0 +1,982 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ rplmain.c
+
+Abstract:
+
+ Contains RPL_main(), RPL service entry point. Initializes and shuts down
+ RPL service.
+
+ Provides similar functionality to rplservr.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#define RPLDATA_ALLOCATE
+#include "local.h"
+#undef RPLDATA_ALLOCATE
+#include "request.h"
+#include "database.h"
+#include "debug.h"
+#include "apisec.h"
+#include <rplnames.h> // RPL_INTERFACE_NAME
+#include "rplsvc_s.h" // rplsvc_ServerIfHandle
+#include "setup.h" // SetupAction()
+
+#define WCSLEN( _x_) ((DWORD) ( sizeof(_x_)/sizeof(WCHAR) - 1))
+#define NET_MSG_FILE L"netmsg.dll"
+#define RPLFILES_STRING L"RPLFILES"
+#define BINFILES_STRING L"\\BINFILES\\"
+#define RPL_DEFAULT_DIRECTORY L"%SystemRoot%\\rpl\\"
+#define RPL_SERVICE_NAME L"RemoteBoot"
+
+#define RPL_SECURITY_OBJECT_CREATED 0x1
+#define RPL_RPC_SERVER_STARTED 0x2
+
+
+DWORD RplMessageGet(
+ IN DWORD MessageId,
+ OUT LPWSTR buffer,
+ IN DWORD Size
+ )
+/*++
+
+Routine Description:
+
+ Fills in the unicode message corresponding to a given message id,
+ provided that a message can be found and that it fits in a supplied
+ buffer.
+
+Arguments:
+
+ MessageId - message id
+ buffer - pointer to caller supplied buffer
+ Size - size (always in bytes) of supplied buffer
+
+Return Value:
+
+ Count of characters, not counting the terminating null character,
+ returned in the buffer. Zero return value indicates failure.
+
+--*/
+{
+ DWORD length;
+ LPVOID lpSource;
+ DWORD dwFlags;
+
+ if ( MessageId < NERR_BASE) {
+ //
+ // Get message from system.
+ //
+ lpSource = NULL; // redundant step according to FormatMessage() spec
+ dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+
+ } else {
+ //
+ // Get message from netmsg.dll.
+ //
+ lpSource = (LPVOID)RG_MessageHandle;
+ dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+
+//#define RPL_ELNK
+#ifdef RPL_ELNK
+ wcscpy( buffer, L"NET2500: ");
+ buffer[ 6] = L'0' + MessageId - NERR_BadDosRetCode;
+ buffer += 9;
+ Size -= 9 * sizeof(WCHAR);
+#endif
+
+ length = FormatMessage(
+ dwFlags, // dwFlags
+ lpSource, // lpSource
+ MessageId, // MessageId
+ 0, // dwLanguageId
+ buffer, // lpBuffer
+ Size, // nSize
+ NULL // lpArguments
+ );
+
+ if ( length == 0) {
+ RG_Error = GetLastError();
+ RplReportEvent( NELOG_RplMessages, NULL, 0, NULL);
+ RPL_RETURN( 0);
+ }
+#ifdef RPL_ELNK
+ length += 9;
+#endif
+ return( length);
+}
+
+
+BOOL RplInitMessages( VOID)
+/*++
+
+Routine Description:
+ Creates an array of NLS (DBCS ??) messages for RPLBOOT.SYS.
+
+Arguments:
+ None.
+
+Return Value:
+ TRUE if success, FALSE otherwise.
+
+--*/
+{
+ WCHAR UnicodeString[ MAX_PATH];
+ DWORD UnicodeStringLength;
+ CHAR DbcsString[ MAX_PATH];
+ PBYTE pByte;
+ DWORD Index;
+ DWORD DbcsStringSize; // not including terminal null byte
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "++InitMessages"));
+
+ RG_DbcsMessageBuffer = RplMemAlloc( RG_MemoryHandle, DBCS_MESSAGE_BUFFER_SIZE);
+ if ( RG_DbcsMessageBuffer == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ //
+ // read NLS messages from message file to patch table
+ //
+ for ( Index = 0, pByte = RG_DbcsMessageBuffer;
+ Index < MESSAGE_TABLE_LENGTH;
+ Index++, pByte += DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
+
+ UnicodeStringLength = RplMessageGet( RG_MessageTable[ Index],
+ UnicodeString, sizeof( UnicodeString));
+ if ( UnicodeStringLength == 0) {
+ return( FALSE);
+ }
+ //
+ // Null terminate the string - redundant ?
+ //
+ UnicodeString[ UnicodeStringLength] = 0;
+
+ DbcsStringSize = WideCharToMultiByte(
+ CP_OEMCP,
+ 0,
+ UnicodeString,
+ UnicodeStringLength,
+ DbcsString,
+ sizeof( DbcsString),
+ NULL, // no default character
+ NULL // no default character flag
+ );
+ if ( DbcsStringSize == 0) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+// #define TEST_MESSAGE_TOO_LONG
+#ifdef TEST_MESSAGE_TOO_LONG
+ if ( DbcsStringSize > 48) {
+ DbcsStringSize = 48;
+ }
+#else
+ //
+ // If message is too long truncate it - leaving one char for the
+ // end of string mark of MS-DOS ('$').
+ //
+ if ( DbcsStringSize >= DBCS_SINGLE_MESSAGE_BUFFER_SIZE) {
+ DbcsStringSize = (DWORD)(DBCS_SINGLE_MESSAGE_BUFFER_SIZE-1);
+ }
+#endif
+ memcpy( pByte, DbcsString, DbcsStringSize);
+ //
+ // The messages returned by DosGetMsg are not nul terminated =>
+ // reset the whole message buffer with end of string marks of MS-DOS.
+ //
+ memset( pByte + DbcsStringSize, '$', DBCS_SINGLE_MESSAGE_BUFFER_SIZE - DbcsStringSize);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOWINIT,( "--InitMessages"));
+ return( TRUE);
+}
+
+
+
+BOOL RplMakeSingleShare(
+ IN LPWSTR NetworkName,
+ IN LPWSTR Path,
+ IN LPWSTR Comment
+ )
+/*++
+
+Routine Description:
+
+ Sets up a file share. It also checks that the existing share is
+ valid (points to the right directory).
+
+Arguments:
+
+ NetworkName - network name of the shared resource
+ Path - local path of the shared resource
+ Comment - comment about the shared resource
+
+Return Value:
+
+ TRUE if successful, else FALSE.
+
+--*/
+{
+ SHARE_INFO_2 ShareInfo2;
+ PSHARE_INFO_2 pShareInfo2 = NULL;
+ BOOL useNetApiBufferFree = TRUE;
+ DWORD Error;
+
+ //
+ // Check if we have already the share.
+ //
+ Error = NetShareGetInfo( NULL, NetworkName, 2, (LPBYTE *)&pShareInfo2);
+
+ if ( Error == NO_ERROR) {
+ //
+ // If the path points to the right place, assume share is O.K.
+ // and return from here. Else, delete this invalid share.
+ //
+ if ( !wcscmp( pShareInfo2->shi2_path, Path )) {
+ Error = NO_ERROR;
+ goto cleanup;
+
+ } else if ( (Error = NetShareDel( NULL, NetworkName, 0))
+ != NO_ERROR) {
+ goto cleanup;
+ }
+
+ } else if ( Error != NERR_NetNameNotFound) {
+ // Unexpected return code from NetShareGetInfo. Bail out.
+ goto cleanup;
+ }
+
+ //
+ // If we arrive here we either did not have the share or we have
+ // just deleted the invalid share. Now set up the correct share.
+ //
+
+ ShareInfo2.shi2_netname = NetworkName;
+ ShareInfo2.shi2_path = Path;
+ ShareInfo2.shi2_remark = Comment;
+
+ ShareInfo2.shi2_type = STYPE_DISKTREE;
+ ShareInfo2.shi2_permissions = 0x3F; // no permission bit set
+ ShareInfo2.shi2_max_uses = SHI_USES_UNLIMITED;
+ ShareInfo2.shi2_current_uses = 0;
+ ShareInfo2.shi2_passwd = NULL;
+
+ Error = NetShareAdd( NULL, 2, (LPBYTE)&ShareInfo2, NULL);
+
+cleanup:
+
+ if ( pShareInfo2) {
+ NetApiBufferFree( (LPVOID) pShareInfo2);
+ }
+
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error = %d", Error));
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+
+BOOL RplConfigured( VOID)
+/*++
+
+Routine Description:
+
+ Verifies that FILE server has been configured to serve RPL clients
+ by checking for the existence of the group RPLUSER, a group
+ that gets created by RPLINST, the last phase of RPL configuration.
+
+Arguments:
+ None.
+
+Return Value:
+
+--*/
+{
+ LPBYTE pbyte = NULL;
+ DWORD Error;
+
+ Error = NetGroupGetInfo( NULL, RPLUSER_GROUP, 0, &pbyte);
+
+ if ( pbyte != NULL) {
+ NetApiBufferFree( pbyte);
+ }
+
+ if (Error == NERR_GroupNotFound) {
+#ifdef NOT_YET
+ Error = NERR_RplNotRplServer; // error mapping
+#else // NOT_YET
+ KdPrint(( "[RplSvc] %ws group not found\n", RPLUSER_GROUP));
+ Error = NO_ERROR; // BUGBUG trundle along for now
+#endif // NOT_YET
+ }
+
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert,( "Error = %d", Error));
+ RG_Error = NERR_RplNotRplServer;
+ return( FALSE);
+ }
+ return( TRUE);
+}
+
+
+BOOL RplInit( OUT PDWORD pStartup)
+/*++
+ pStartup - Startup actions flag, read from registry.
+--*/
+{
+ DWORD Error;
+ DWORD Size;
+ SC_HANDLE ControlManagerHandle;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++RplInit"));
+
+ Error = RplMemInit( &RG_MemoryHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ return( FALSE);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_MEMORY,(
+ "MakeReinit: RG_MemoryHandle=0x%x", RG_MemoryHandle));
+
+ RG_DirectoryLength = sizeof(RG_Directory)/sizeof(WCHAR);
+ if ( RplRegRead( pStartup, &RG_BackupInterval, &RG_MaxWorkerCount,
+ RG_Directory, &RG_DirectoryLength) != NO_ERROR) {
+ RG_Error = NERR_RplBadRegistry;
+ return( FALSE);
+ }
+
+ if ( !RplDbInit()) {
+ RG_Error = NERR_RplBadDatabase;
+ return( FALSE);
+ }
+
+ ControlManagerHandle = OpenSCManager( NULL, L"ServicesActive", 0);
+ if ( ControlManagerHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ RG_ServiceHandle = OpenService( ControlManagerHandle,
+ RPL_SERVICE_NAME,
+ SERVICE_STOP // desired access
+ );
+ if ( RG_ServiceHandle == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ RG_TerminateNowEvent = CreateEvent(
+ NULL, // No security attributes
+ TRUE, // Must be manually reset
+ FALSE, // Initially not signaled
+ NULL); // No name
+ if ( RG_TerminateNowEvent == NULL ) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // We use manual reset. With automatic reset, when two request threads
+ // are both waiting for this event, if several worker threads exit at
+ // once, only one request thread may be awaken. Other request thread
+ // may stay asleep for a long time, until next worker thread exits.
+ //
+ RG_EventWorkerCount = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // use manual reset
+ FALSE, // initial value is not-signalled
+ NULL); // no name
+ if ( RG_EventWorkerCount == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+
+ if ( !RplConfigured()) {
+ return( FALSE);
+ }
+
+ //
+ // Note that upon successful return: Size == wcslen( RG_ComputerName)
+ //
+ Size = sizeof( RG_ComputerName);
+ if ( !GetComputerName( RG_ComputerName, &Size)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ RPL_ASSERT( Size == wcslen( RG_ComputerName));
+
+ //
+ // Initialize RG_UncRplfiles which is used in fit file replacements
+ // and RG_DiskRplfiles which is used for api disk operations.
+ //
+ Size *= sizeof( WCHAR);
+ Size += sizeof(DOUBLE_BACK_SLASH_STRING) + sizeof(RPLFILES_STRING);
+ RG_UncRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_UncRplfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_UncRplfiles, DOUBLE_BACK_SLASH_STRING);
+ wcscat( RG_UncRplfiles, RG_ComputerName);
+ wcscat( RG_UncRplfiles, L"\\");
+ wcscat( RG_UncRplfiles, RPLFILES_STRING);
+
+ Size = RG_DirectoryLength * sizeof(WCHAR) + sizeof(RPLFILES_STRING);
+ RG_DiskRplfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_DiskRplfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_DiskRplfiles, RG_Directory);
+ wcscat( RG_DiskRplfiles, RPLFILES_STRING);
+
+ Size += WCSLEN( BINFILES_STRING) * sizeof(WCHAR);
+ RG_DiskBinfiles = RplMemAlloc( RG_MemoryHandle, Size);
+ if ( RG_DiskBinfiles == NULL) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+ wcscpy( RG_DiskBinfiles, RG_DiskRplfiles);
+ wcscat( RG_DiskBinfiles, BINFILES_STRING);
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // Load file containing network messages.
+ //
+ RG_MessageHandle = LoadLibrary( NET_MSG_FILE);
+ if ( RG_MessageHandle == NULL) {
+ RG_Error = GetLastError();
+ RplReportEvent( NELOG_Init_OpenCreate_Err, NET_MSG_FILE,
+ sizeof( RG_Error), (PBYTE)&RG_Error);
+ RPL_RETURN( FALSE);
+ }
+
+ //
+ // Share the default rplfiles share, should have been done earlier ?
+ //
+ if ( !RplMakeSingleShare( RPLFILES_STRING, RG_DiskRplfiles, RPL_REMARK)){
+ RG_Error = NERR_RplRplfilesShare;
+ return( FALSE);
+ }
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_RETURN( FALSE);
+ }
+#endif
+
+ //
+ // RplInitMessages() must be called before RplStartAdapters() because
+ // it initializes RG_DbcsMessageBuffer() which must be present as soon
+ // as adapters are started. Otherwise, a very quick boot request by
+ // RPL client would trap the service in RplMakePatch().
+ //
+ if ( !RplInitMessages()) {
+ return( FALSE);
+ }
+
+ if ( !RplStartAdapters()) {
+ return( FALSE);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--RplInit"));
+ return( TRUE);
+}
+
+
+VOID RplCleanup( VOID)
+/*++
+
+Routine Description:
+
+ Cleanup all global resources.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD ErrorCode = NO_ERROR;
+
+ RplDbTerm();
+ //
+ // Free RPL critical sections.
+ //
+ DeleteCriticalSection( &RG_ProtectRcbList);
+ DeleteCriticalSection( &RG_ProtectTerminationList);
+ DeleteCriticalSection( &RG_ProtectRequestList);
+ DeleteCriticalSection( &RG_ProtectWorkerCount);
+ DeleteCriticalSection( &RG_ProtectServiceStatus);
+ DeleteCriticalSection( &RG_ProtectServerHandle);
+ DeleteCriticalSection( &RG_ProtectRequestSession);
+ DeleteCriticalSection( &RG_ProtectWorkerSession);
+ DeleteCriticalSection( &RG_ProtectDatabase);
+}
+
+
+
+VOID RplControlHandler( IN DWORD OpCode)
+/*++
+
+Routine Description:
+
+ Process and respond to a control signal from the service controller.
+
+Arguments:
+
+ OpCode - Supplies a value which specifies the action for the RPL
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KdPrint(( "[RplSvc]++ ControlHandler\n"));
+
+ switch (OpCode) {
+
+ case SERVICE_CONTROL_PAUSE:
+#ifdef RPL_DEBUG
+ DbgUserBreakPoint(); // for debugging
+#endif
+ EnterCriticalSection( &RG_ProtectServiceStatus);
+ RG_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
+ LeaveCriticalSection( &RG_ProtectServiceStatus);
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ EnterCriticalSection( &RG_ProtectServiceStatus);
+ RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ LeaveCriticalSection( &RG_ProtectServiceStatus);
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+
+ if (RG_ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ KdPrint(( "[RplSvc]ControlHandler: stopping remote boot service...\n"));
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ RG_ServiceStatus.dwCheckPoint = 1;
+ RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
+
+ //
+ // Send the status response.
+ //
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", RG_Error));
+ NOTHING; // ignore this error
+ }
+#endif
+
+ if (! SetEvent( RG_TerminateNowEvent)) {
+
+ KdPrint(( "[RplSvc]ControlHandler: error setting"
+ " TerminateNowEvent, error=%d\n",
+ GetLastError()));
+ RPL_ASSERT( FALSE);
+ }
+
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ default:
+ KdPrint((
+ "[RplSvc] ControlHandler: unknown remote boot service control,"
+ " OpCode=0x%x\n",
+ OpCode));
+ break;
+ }
+
+ //
+ // Send the status response.
+ //
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // ignore this error
+ }
+#endif
+}
+
+
+
+VOID RplInitGlobals( VOID)
+{
+ //
+ // Initializes all global variables that are "variable".
+ //
+
+ RG_WorkerCount = 0;
+ RG_TerminationListBase = NULL;
+ RG_MessageHandle = NULL;
+#ifdef RPL_DEBUG
+ RG_Debug = (DWORD)-1;
+ RG_BootCount = 0;
+#endif // RPL_DEBUG
+
+ //
+ // Values of globals below may be overriden by user in lanman.ini & command line.
+ //
+
+ RG_Directory[ 0] = 0;
+ RG_DirectoryLength = 0;
+ RG_ReadChecksum = FALSE;
+ RG_CodePageBuffer = NULL;
+ RG_CodePageSize = 0;
+
+ RG_TerminateNowEvent = NULL;
+
+ RG_ComputerName[ 0] = 0;
+ RG_UncRplfiles = NULL;
+ RG_DiskRplfiles = NULL;
+
+ RG_ServerHandle = 0;
+
+ RG_pRequestParams = NULL;
+
+ InitializeCriticalSection( &RG_ProtectRcbList);
+ InitializeCriticalSection( &RG_ProtectTerminationList);
+ InitializeCriticalSection( &RG_ProtectRequestList);
+ InitializeCriticalSection( &RG_ProtectWorkerCount);
+ InitializeCriticalSection( &RG_ProtectServiceStatus);
+ InitializeCriticalSection( &RG_ProtectServerHandle);
+ InitializeCriticalSection( &RG_ProtectRequestSession);
+ InitializeCriticalSection( &RG_ProtectWorkerSession);
+ InitializeCriticalSection( &RG_ProtectDatabase);
+}
+
+
+VOID ShutdownRequestParams( VOID)
+{
+ PRPL_REQUEST_PARAMS pRequestParams;
+ DWORD status;
+
+ for ( pRequestParams = RG_pRequestParams;
+ pRequestParams != NULL;
+ pRequestParams = pRequestParams->pRequestParams) {
+ if ( pRequestParams->ThreadHandle != NULL) {
+ status = WaitForSingleObject( pRequestParams->ThreadHandle, INFINITE);
+ if ( status != 0) {
+ RplDump( ++RG_Assert, ( "pRequestParams=0x%x, status=0x%x",
+ pRequestParams, status==WAIT_FAILED ? GetLastError() : status));
+ }
+ if ( !CloseHandle( pRequestParams->ThreadHandle)) {
+ RplDump( ++RG_Assert, ( "pRequestParams=0x%x, error=%d",
+ pRequestParams, GetLastError()));
+ }
+ pRequestParams->ThreadHandle = NULL;
+ }
+ }
+}
+
+
+VOID RPL_main(
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ )
+/*++
+
+Routine Description:
+
+ Main procedure of the program. This is the main RPL worker thread.
+ Purpose:
+
+ - start the initialization thread
+ - register signal handler
+
+Arguments:
+
+ argc - parameter count
+ argv - array of pointers to parameters
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD Error;
+ DWORD InitState;
+ DWORD StartupFlags;
+
+ UNREFERENCED_PARAMETER( argc);
+ UNREFERENCED_PARAMETER( argv);
+
+ InitState = 0;
+
+ RG_Error = NO_ERROR;
+ RG_ServiceStatus.dwServiceType = SERVICE_WIN32;
+ RG_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ RG_ServiceStatus.dwControlsAccepted = 0;
+ RG_ServiceStatus.dwCheckPoint = 1;
+ RG_ServiceStatus.dwWaitHint = RPL_WAIT_HINT_TIME;
+
+ // This should be the FIRST thing we do.
+
+#ifdef RPL_DEBUG
+ RplDebugInitialize();
+#endif // RPL_DEBUG
+
+
+ SET_SERVICE_EXITCODE(
+ RG_Error,
+ RG_ServiceStatus.dwWin32ExitCode,
+ RG_ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ RplInitGlobals(); // make sure we do not trap during cleanup
+
+#ifndef RPL_NO_SERVICE
+ RG_ServiceStatusHandle = RegisterServiceCtrlHandler(
+ SERVICE_RIPL,
+ RplControlHandler
+ );
+ if( RG_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)NULL) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+#endif
+
+ if ( !RplInit( &StartupFlags)) {
+ goto main_exit;
+ }
+
+ //
+ // Create remote boot service security object
+ //
+ Error = RplCreateSecurityObject();
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ goto main_exit;
+ }
+ InitState |= RPL_SECURITY_OBJECT_CREATED;
+
+ //
+ // Initialize the schedule service to receive RPC requests.
+ // Note that interface name is defined in rplsvc.idl file.
+ //
+ Error = NetpStartRpcServer( RPL_INTERFACE_NAME, rplsvc_ServerIfHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+ InitState |= RPL_RPC_SERVER_STARTED;
+
+ //
+ // We are done with starting the Remoteboot service. Tell Service
+ // Controller our new status.
+ //
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ RG_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
+ RG_ServiceStatus.dwCheckPoint = 0;
+ RG_ServiceStatus.dwWaitHint = 0;
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RG_Error = GetLastError();
+ RPL_ASSERT( FALSE);
+ goto main_exit;
+ }
+#endif
+
+ //
+ // The server is running. If the registry specified any
+ // special startup actions, perform them now. Note that
+ // it will not be possible to stop the service before these
+ // actions are complete. Also, note that SetupAction() does
+ // all necessary event logging & properly updates its argument
+ // even in case of failures.
+ //
+
+ if ( StartupFlags & RPL_SPECIAL_ACTIONS) {
+ DWORD NewStartupFlags = StartupFlags;
+ (VOID)SetupAction( &NewStartupFlags, FALSE); // partial backup if any
+ if ( NewStartupFlags != StartupFlags) {
+ (VOID)RplRegSetPolicy( NewStartupFlags);
+ }
+ }
+
+ //
+ // We started successfully. Wait for somebody to tell us it
+ // is time to die.
+ //
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_SERVICE,(
+ "Successful startup. Wait on TerminateNowEvent."));
+
+#define ONE_HOUR (60 * 60 * 1000L) // in milliseconds
+ for ( ; ; ) {
+ DWORD Status;
+ Status = WaitForSingleObject( RG_TerminateNowEvent,
+ RG_BackupInterval == 0 ? INFINITE : RG_BackupInterval * ONE_HOUR);
+ if ( Status != WAIT_TIMEOUT) {
+ RPL_ASSERT( Status == 0);
+#ifdef NOT_YET
+ //
+ // Backup is disabled here because in the field users will most
+ // likely use CTRL-ALT-DEL to shutdown the system & RPL service.
+ // In that case we do no enter this code path and to keep all
+ // remoteboot shutdown sequences similar backup is disabled here
+ // as well. By not doing backup here we also avoid problems due
+ // to full backup taking too long or incremental backup being
+ // buggy (jet bugs).
+ //
+ //
+ // In case of regular shutdown we do incremental backup so that
+ // user does not get an impression we are "stuck". Also, the
+ // backup is done now & not later in RplDbTerm() just in case
+ // we get in a state where we hang for ever in
+ // ShutdownWorkerParams().
+ //
+ RplBackupDatabase( Status == 0 ? FALSE : TRUE);
+#endif
+ break;
+ }
+ //
+ // Do full backup if not on shutdown path.
+ //
+ RplBackupDatabase( TRUE);
+ }
+
+main_exit:
+
+ //
+ // If we come here, then it is time to die. Wait until all
+ // our children (request threads) have been terminated.
+ // And of course, request threads will in turn wail until all
+ // of their children (worker threads) get terminated.
+ //
+ // Because of a cumbersome inherited Rpld* interface, request
+ // threads may wait for ever on Rpld* events. The only way to get
+ // them out is to close the adapters.
+ //
+ RplCloseAdapters(); // cumbersome Rpld* interface
+
+ ShutdownRequestParams();
+
+ //
+ // Close RPC server. Jet resources must be available at this
+ // point because rundown routines need them.
+ //
+ if ( InitState & RPL_RPC_SERVER_STARTED) {
+ Error = NetpStopRpcServer( rplsvc_ServerIfHandle);
+ if ( Error != NO_ERROR) {
+ RG_Error = Error;
+ RPL_ASSERT( FALSE);
+ }
+ }
+
+ if ( InitState & RPL_SECURITY_OBJECT_CREATED) {
+ RplDeleteSecurityObject();
+ }
+
+
+ RplCleanup(); // nobody alive but us now, clean up and die
+
+#ifdef RPL_DEBUG
+ RplDebugDelete();
+#endif // RPL_DEBUG
+
+ //
+ // Set the service state to uninstalled, and tell the service controller.
+ //
+
+ RG_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ RG_ServiceStatus.dwControlsAccepted = 0;
+
+ SET_SERVICE_EXITCODE(
+ RG_Error,
+ RG_ServiceStatus.dwWin32ExitCode,
+ RG_ServiceStatus.dwServiceSpecificExitCode
+ );
+
+ RG_ServiceStatus.dwCheckPoint = 0;
+ RG_ServiceStatus.dwWaitHint = 0;
+
+#ifndef RPL_NO_SERVICE
+ if ( !SetServiceStatus( RG_ServiceStatusHandle, &RG_ServiceStatus)) {
+ RplDump( ++RG_Assert, ( "Error = ", GetLastError()));
+ NOTHING; // ignore this error
+ }
+#endif
+}
+
+
+BOOL RplServiceAttemptStop( VOID)
+{
+ SERVICE_STATUS ServiceStatus;
+ if ( ControlService( RG_ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus)) {
+ return( TRUE); // all done
+ }
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ if ( ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ //
+ // We arrive here if control handler already received
+ // SERVICE_CONTROL_STOP request from somewhere else.
+ //
+ return( TRUE);
+ }
+ return( FALSE);
+}
+
+
+
+
diff --git a/private/net/svcdlls/rpl/server/rplrest.c b/private/net/svcdlls/rpl/server/rplrest.c
new file mode 100644
index 000000000..f570bb866
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplrest.c
@@ -0,0 +1,36 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplrest.c
+
+Abstract:
+
+ This file contains program to test RPL JetRestore() call.
+
+Author:
+
+ Vladimiv Z. Vulovic (vladimv) 17-June-1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include "local.h"
+
+#define BACKUP_PATH "d:\\nt689f\\rpl\\backup"
+
+
+DWORD _CRTAPI1 main( int argc, char **argv)
+{
+ JET_ERR JetError;
+ JetError = JetRestore( BACKUP_PATH, 0, NULL, 0);
+ printf( "JetError=%d\n", JetError);
+ return(0);
+}
diff --git a/private/net/svcdlls/rpl/server/rplsvc.c b/private/net/svcdlls/rpl/server/rplsvc.c
new file mode 100644
index 000000000..34a377a11
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplsvc.c
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ rplsvc.c
+
+Abstract:
+
+ This is the main routine for the Remote Program Load Service.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 10 - Februrary - 1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 10-Feb-1993 vladimv
+ created
+
+--*/
+
+#include "local.h"
+#include <rpcutil.h> // NetpInitRpcServer()
+#include <secobj.h> // NetpCreateWellKnownSids()
+
+//
+// Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
+//
+// Add new service entries here.
+//
+
+SERVICE_TABLE_ENTRY RPLServiceDispatchTable[] = {
+ { SERVICE_RIPL, RPL_main },
+ { NULL, NULL }
+};
+
+
+
+VOID _CRTAPI1
+main (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This is a main routine for the LANMan Remote Program Load Services.
+
+ It basically sets up the ControlDispatcher and, on return, exits from
+ this main thread. The call to NetServiceStartCtrlDispatcher does
+ not return until all services have terminated, and this process can
+ go away.
+
+ It will be up to the ControlDispatcher thread to start/stop/pause/continue
+ any services. If a service is to be started, it will create a thread
+ and then call the main routine of that service.
+
+
+Arguments:
+
+ Anything passed in from the "command line". Currently, NOTHING.
+
+Return Value:
+
+ NONE
+
+Note:
+
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ //
+ // Create well-known SIDs
+ //
+ if (! NT_SUCCESS (ntstatus = NetpCreateWellKnownSids(NULL))) {
+ KdPrint((
+ "[RplSvc] main: Failed to create well-known SIDs, ntstatus=0x%x\n",
+ ntstatus
+ ));
+ return;
+ }
+
+
+ //
+ // Initialize the RpcServer Locks.
+ //
+
+ NetpInitRpcServer();
+
+ //
+ // Call ServiceStartCtrlDispatcher to set up the control interface.
+ // The API won't return until all services have been terminated. At that
+ // point, we just exit.
+ //
+
+#ifndef RPL_NO_SERVICE
+ if ( !StartServiceCtrlDispatcher ( RPLServiceDispatchTable)) {
+ DWORD error = GetLastError();
+ //
+ // RPL service will eventually go in the same exe with all
+ // other services. Thus, no need here to log anything.
+ //
+ KdPrint((
+ "[RplSvc] main: StartServiceCtrlDispatcher() fails with error=%d\n",
+ error
+ ));
+ }
+#else
+ RPL_main( 1, (LPWSTR *)NULL);
+#endif
+
+ ExitProcess(0);
+}
diff --git a/private/net/svcdlls/rpl/server/rplsvc.rc b/private/net/svcdlls/rpl/server/rplsvc.rc
new file mode 100644
index 000000000..1fffb1423
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/rplsvc.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "RemoteBoot Service DLL"
+#define VER_INTERNALNAME_STR "RPLSVC.DLL"
+#define VER_ORIGINALFILENAME_STR "RPLSVC.DLL"
+
+#include "common.ver"
+
diff --git a/private/net/svcdlls/rpl/server/security.c b/private/net/svcdlls/rpl/server/security.c
new file mode 100644
index 000000000..2827437d9
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/security.c
@@ -0,0 +1,1005 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ security.c
+
+Abstract:
+
+ Security addons to the tree-manipulation module
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+ Vladimir Z. Vulovic (vladimv) 06 - May - 1994
+ Security fix: added new api + major reorg.
+
+--*/
+
+
+#include "local.h"
+#include "rpldb.h" // Call(), CallM()
+#include "db.h"
+#include "dblib.h" // RplFilterFirst()
+#include "tree.h"
+#include "treei.h"
+#include "wksta.h" // WKSTA_WkstaName
+#include "profile.h" // PROFILE_ProfileName
+#include <ntseapi.h>
+#include <ntrtl.h> // RtlAllocateAndInitializeSid
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <nturtl.h> // see IsNTFS
+#include <lmserver.h> // NetServerGetInfo
+
+
+typedef enum _RPL_SD_BLOCK_TYPE {
+
+ RPL_SD_BLOCK_TYPE_ADMINONLY = 0,// Only RPLADMIN gets access
+
+ RPL_SD_BLOCK_TYPE_ALLUSERS, // RPLADMIN gets full access, RPLUSER
+ // gets RX access
+
+ RPL_SD_BLOCK_TYPE_ONEUSER // RPLADMIN gets full access, one
+ // wksta account gets RWXCDA access
+} RPL_SD_BLOCK_TYPE;
+
+/*
+ * This is the block of information which is retained between the recursive
+ * calls in a single RplDoTree run. It remembers the SECURITY_DESCRIPTOR
+ * which was assigned to the last file/directory, so if the next
+ * file/directory wants the same permissions (as is usually the case)
+ * we do not have to fetch everything all over again. It also contains
+ * some information about what kind of SECURITY_DESCRIPTOR is currently
+ * stored, plus items needed to get a different SECURITY_DESCRIPTOR.
+ */
+typedef struct _RPL_SD_BLOCK {
+ BOOL SkipThisTree; // must be the first element
+ PSECURITY_DESCRIPTOR psd;
+ PACL paclDacl;
+ BOOL MonitorSecurityType;
+ RPL_SD_BLOCK_TYPE SecurityType;
+ //
+ // CODEWORK do we need to keep these sids around
+ //
+ PSID psidSystem;
+ PSID psidAdministrators;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoRPLUSERAccountDomain;
+ SAM_HANDLE hsamRPLUSERAccountDomain;
+ POLICY_ACCOUNT_DOMAIN_INFO * pinfoWkstaAccountDomain;
+ SAM_HANDLE hsamWkstaAccountDomain;
+} RPL_SD_BLOCK, *PRPL_SD_BLOCK;
+
+//
+// The DACL we assign to files has 2 or 3 ACEs.
+//
+#define RPL_INDEX_SYSTEM_ACE 0
+#define RPL_INDEX_ADMINS_ACE 1
+#define RPL_INDEX_CHANGEABLE_ACE 2
+
+
+
+DWORD IsNTFS(
+ IN PWCHAR Resource,
+ OUT BOOL * pfIsNTFS
+ )
+/*++
+
+ NAME: IsNTFS
+
+ SYNOPSIS: This function checks the given file resource and attempts to
+ determine if it is on an NTFS partition.
+
+ ENTRY: Resource - Pointer to file/directory name in the form
+ "X:\aaa\bbb\ccc
+ pfIsNTFS - Pointer to BOOL that will receive the results
+
+ RETURNS: NO_ERROR if successful, error code otherwise
+
+ NOTES:
+
+ HISTORY:
+ JonN 23-Feb-1994 Copied from acledit\ntfsacl.cxx
+
+--*/
+{
+ DWORD Error = NO_ERROR ;
+ HANDLE hDrive = NULL ;
+ WCHAR awchDosDriveName[4];
+ WCHAR awchNtDriveName[40];
+ UNICODE_STRING unistrNtDriveName;
+ BYTE buffFsInfo[ sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 200 ] ;
+ PFILE_FS_ATTRIBUTE_INFORMATION FsInfo =
+ (PFILE_FS_ATTRIBUTE_INFORMATION)buffFsInfo ;
+ OBJECT_ATTRIBUTES oa ;
+ IO_STATUS_BLOCK StatusBlock ;
+
+ RPL_ASSERT( Resource != NULL
+ && wcslen(Resource) >= 3
+ && pfIsNTFS != NULL ) ;
+
+ *pfIsNTFS = FALSE ;
+
+ //
+ // Determine the NT drive name
+ //
+
+ awchDosDriveName[0] = Resource[0];
+ awchDosDriveName[1] = Resource[1];
+ awchDosDriveName[2] = Resource[2];
+ awchDosDriveName[3] = L'\0';
+ RPL_ASSERT( awchDosDriveName[1] == L':'
+ && awchDosDriveName[2] == L'\\' );
+
+ unistrNtDriveName.Buffer = awchNtDriveName;
+ unistrNtDriveName.Length = sizeof(awchNtDriveName);
+ unistrNtDriveName.MaximumLength = sizeof(awchNtDriveName);
+
+ if (!RtlDosPathNameToNtPathName_U( awchDosDriveName,
+ &unistrNtDriveName,
+ NULL,
+ NULL)){
+ RPL_ASSERT( FALSE ) ;
+ Error = ERROR_NOT_ENOUGH_MEMORY ;
+ goto cleanup;
+ }
+ RPL_ASSERT( unistrNtDriveName.Length < unistrNtDriveName.MaximumLength );
+ unistrNtDriveName.Buffer[unistrNtDriveName.Length/sizeof(WCHAR)] = L'\0';
+
+ //
+ // Open the NT volume and query volume information
+ //
+
+ InitializeObjectAttributes( &oa,
+ &unistrNtDriveName,
+ OBJ_CASE_INSENSITIVE,
+ 0,
+ 0 );
+ Error = NtOpenFile( &hDrive,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &oa,
+ &StatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_ALERT);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = NtQueryVolumeInformationFile(
+ hDrive,
+ &StatusBlock,
+ (PVOID) FsInfo,
+ sizeof(buffFsInfo),
+ FileFsAttributeInformation );
+ if (Error != NO_ERROR) {
+ if (Error == ERROR_ACCESS_DENIED) {
+ RplDump( ++RG_Assert, ( "(access denied) assuming the file system is NTFS"));
+ Error = NO_ERROR ;
+ *pfIsNTFS = TRUE ;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ }
+ goto cleanup;
+ }
+
+ if ( FsInfo->FileSystemAttributes & FILE_PERSISTENT_ACLS){
+ *pfIsNTFS = TRUE ;
+ }
+
+cleanup:
+
+ /* Close the volume if we ever opened it
+ */
+ if ( hDrive != NULL){
+ NtClose( hDrive );
+ }
+
+ return( Error) ;
+}
+
+
+
+DWORD AddInheritableAce(
+ IN OUT ACL * pacl,
+ IN ACCESS_MASK mask,
+ IN SID * psid
+ )
+/*++
+ Add an ACCESS_ALLOWED_ACE to the ACL, with all inheritance flags set
+--*/
+{
+ DWORD Error = NO_ERROR;
+ ACCESS_ALLOWED_ACE * pace = NULL;
+
+ if ( !AddAccessAllowedAce( pacl, ACL_REVISION, mask, psid )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // new ACE should be the last ACE
+ //
+ if ( !GetAce( pacl, (pacl->AceCount) - 1, &pace )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ RPL_ASSERT( EqualSid( psid, &(pace->SidStart)) );
+
+ (pace->Header.AceFlags) |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE);
+
+cleanup:
+
+ return( Error);
+}
+
+
+VOID DeInitSdBlock( IN RPL_SD_BLOCK * psdblock)
+{
+ if (psdblock->psd != NULL) {
+ RplMemFree( RG_MemoryHandle, psdblock->psd );
+ }
+ if (psdblock->psidSystem != NULL) {
+ RtlFreeSid( psdblock->psidSystem );
+ }
+ if (psdblock->psidAdministrators != NULL) {
+ RtlFreeSid( psdblock->psidAdministrators );
+ }
+ if ( psdblock->pinfoRPLUSERAccountDomain != NULL) {
+ LsaFreeMemory( psdblock->pinfoRPLUSERAccountDomain);
+ }
+ if (psdblock->hsamRPLUSERAccountDomain != NULL) {
+ SamCloseHandle( psdblock->hsamRPLUSERAccountDomain);
+ }
+ if ( psdblock->pinfoWkstaAccountDomain != NULL
+ && psdblock->pinfoWkstaAccountDomain !=
+ psdblock->pinfoRPLUSERAccountDomain) {
+ LsaFreeMemory( psdblock->pinfoWkstaAccountDomain);
+ }
+ if ( psdblock->hsamWkstaAccountDomain != NULL
+ && psdblock->hsamWkstaAccountDomain !=
+ psdblock->hsamRPLUSERAccountDomain) {
+ SamCloseHandle( psdblock->hsamWkstaAccountDomain);
+ }
+}
+
+
+DWORD GetDomains(
+ OUT SAM_HANDLE * phsamWkstaAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoWkstaAccountDomain,
+ OUT SAM_HANDLE * phsamRPLUSERAccountDomain,
+ OUT POLICY_ACCOUNT_DOMAIN_INFO ** ppinfoRPLUSERAccountDomain,
+ IN ACCESS_MASK DesiredAccess
+ )
+/*--
+ Get a SAM_HANDLE to the account domain, and other information about
+ the account domain. This routine uses SAM and LSA directly.
+--*/
+{
+ DWORD Error = NO_ERROR;
+ SAM_HANDLE hsamAccountServer = NULL;
+ SAM_HANDLE hsamLocalServer = NULL;
+ LSA_HANDLE hlsaLocal = NULL;
+ LSA_HANDLE hlsaPDC = NULL;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+
+ BOOL fIsPrimaryDC = FALSE;
+ BOOL fIsBackupDC = FALSE;
+ BOOL fIsNotDC = FALSE;
+
+ SERVER_INFO_101 * psi101 = NULL;
+ POLICY_PRIMARY_DOMAIN_INFO * pinfoPrimaryDomain = NULL;
+ WCHAR awchPrimaryDomain[ DNLEN+1 ];
+ WCHAR * pwchPDC = NULL;
+ UNICODE_STRING unistrPDC;
+
+ RPL_ASSERT( phsamWkstaAccountDomain != NULL );
+ RPL_ASSERT( ppinfoWkstaAccountDomain != NULL );
+ RPL_ASSERT( phsamRPLUSERAccountDomain != NULL );
+ RPL_ASSERT( ppinfoRPLUSERAccountDomain != NULL );
+
+ //
+ // Set up object attributes (borrowed from uintlsa.cxx)
+ //
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ //
+ // Determine whether this is a PDC, BDC or other server.
+ //
+
+ Error = NetServerGetInfo( NULL, 101, (LPBYTE *)&psi101 );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ RPL_ASSERT( psi101 != NULL );
+ fIsPrimaryDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_CTRL);
+ fIsBackupDC = !!(psi101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL);
+ fIsNotDC = !(psi101->sv101_type & (SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL));
+
+ //
+ // Get a local LSA handle
+ //
+
+ Error = LsaOpenPolicy( NULL,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaLocal );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ //
+ // If this is not a DC, get the name of the primary domain and
+ // determine whether this is a ServerNt/WinNt machine on a workgroup.
+ //
+
+ if (!fIsPrimaryDC) {
+ Error = LsaQueryInformationPolicy( hlsaLocal,
+ PolicyPrimaryDomainInformation,
+ &pinfoPrimaryDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ if (pinfoPrimaryDomain->Sid == NULL) {
+ //
+ // This is a ServerNt/WinNt machine on a workgroup. Pretend
+ // this is a Primary DC from now on.
+ //
+ fIsPrimaryDC = TRUE;
+ fIsBackupDC = fIsNotDC = FALSE;
+ }
+ }
+
+ //
+ // If this is not a PDC, get the name of a primary domain PDC
+ // and reload the LSA handle
+ //
+
+ if (!fIsPrimaryDC) {
+ RPL_ASSERT( DNLEN*sizeof(WCHAR) >= pinfoPrimaryDomain->Name.Length );
+ memcpy( awchPrimaryDomain,
+ pinfoPrimaryDomain->Name.Buffer,
+ pinfoPrimaryDomain->Name.Length ); // this is in bytes
+ awchPrimaryDomain[ pinfoPrimaryDomain->Name.Length / sizeof(WCHAR) ]
+ = L'\0';
+
+ Error = NetGetDCName( NULL,
+ awchPrimaryDomain,
+ (LPBYTE *)&pwchPDC );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ unistrPDC.Buffer = pwchPDC;
+ unistrPDC.Length = wcslen( pwchPDC ) * sizeof(WCHAR);
+ unistrPDC.MaximumLength = unistrPDC.Length + sizeof(WCHAR);
+
+ Error = LsaOpenPolicy( &unistrPDC,
+ &oa,
+ GENERIC_EXECUTE,
+ &hlsaPDC );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ //
+ // Load information on the RPLUSERAccount domain
+ //
+
+ Error = LsaQueryInformationPolicy( (fIsBackupDC) ? hlsaPDC : hlsaLocal,
+ PolicyAccountDomainInformation,
+ ppinfoRPLUSERAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Load a SAM handle on the RPLUSERAccount domain
+ //
+
+ Error = SamConnect( (fIsBackupDC) ? &unistrPDC : NULL,
+ &hsamLocalServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = SamOpenDomain( hsamLocalServer,
+ DesiredAccess,
+ (*ppinfoRPLUSERAccountDomain)->DomainSid,
+ phsamRPLUSERAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // If this is not a DC, load information and a SAM handle on
+ // the RPLUSERAccount domain
+ //
+
+ if (!fIsNotDC) {
+ *phsamWkstaAccountDomain = *phsamRPLUSERAccountDomain;
+ *ppinfoWkstaAccountDomain = *ppinfoRPLUSERAccountDomain;
+ } else {
+ Error = LsaQueryInformationPolicy( hlsaPDC,
+ PolicyAccountDomainInformation,
+ ppinfoWkstaAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Get server handle
+ //
+
+ Error = SamConnect( &unistrPDC,
+ &hsamAccountServer,
+ SAM_SERVER_LOOKUP_DOMAIN,
+ NULL );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ Error = SamOpenDomain( hsamAccountServer,
+ DesiredAccess,
+ (*ppinfoWkstaAccountDomain)->DomainSid,
+ phsamWkstaAccountDomain );
+ if ( Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+
+cleanup:
+
+ if ( hsamLocalServer != NULL) {
+ SamCloseHandle( hsamLocalServer);
+ }
+
+ if ( hsamAccountServer != NULL) {
+ SamCloseHandle( hsamAccountServer);
+ }
+
+ if ( hlsaLocal != NULL) {
+ LsaClose( hlsaLocal);
+ }
+
+ if ( hlsaPDC != NULL) {
+ LsaClose( hlsaPDC);
+ }
+
+ if ( pinfoPrimaryDomain != NULL) {
+ LsaFreeMemory( pinfoPrimaryDomain);
+ pinfoPrimaryDomain = NULL;
+ }
+
+ if ( pwchPDC != NULL ) {
+ NetApiBufferFree( (LPBYTE)pwchPDC );
+ pwchPDC = NULL;
+ }
+
+ if ( psi101 != NULL ) {
+ NetApiBufferFree( (LPBYTE)psi101 );
+ psi101 = NULL;
+ }
+
+ if ( Error != NO_ERROR
+ && (*ppinfoWkstaAccountDomain) != NULL
+ && (*ppinfoWkstaAccountDomain) != (*ppinfoRPLUSERAccountDomain) ) {
+ LsaFreeMemory( (*ppinfoWkstaAccountDomain));
+ *ppinfoWkstaAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR
+ && (*phsamWkstaAccountDomain) != NULL
+ && (*phsamWkstaAccountDomain) != (*phsamRPLUSERAccountDomain) ) {
+ SamCloseHandle( *phsamWkstaAccountDomain);
+ *phsamWkstaAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && (*ppinfoRPLUSERAccountDomain) != NULL) {
+ LsaFreeMemory( (*ppinfoRPLUSERAccountDomain));
+ *ppinfoRPLUSERAccountDomain = NULL;
+ }
+
+ if ( Error != NO_ERROR && (*phsamRPLUSERAccountDomain) != NULL) {
+ SamCloseHandle( *phsamRPLUSERAccountDomain);
+ *phsamRPLUSERAccountDomain = NULL;
+ }
+
+ return( Error);
+}
+
+
+DWORD MyBuildSid(
+ OUT PSID * ppsidAccountSid,
+ IN POLICY_ACCOUNT_DOMAIN_INFO * pinfoAccountDomain,
+ IN DWORD ulRid
+ )
+{
+ DWORD Error = 0;
+ DWORD cbNewSid = 0;
+ PUCHAR pcSubAuthorityCount = NULL;
+ PDWORD pdwLastSubAuthority = NULL;
+
+ RPL_ASSERT( ppsidAccountSid != NULL && pinfoAccountDomain != NULL && ulRid != 0 );
+
+ cbNewSid = GetLengthSid( pinfoAccountDomain->DomainSid) + sizeof(DWORD);
+ *ppsidAccountSid = RplMemAlloc( RG_MemoryHandle, cbNewSid );
+ if ( *ppsidAccountSid == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ goto cleanup;
+ }
+
+ if ( !CopySid( cbNewSid, *ppsidAccountSid, pinfoAccountDomain->DomainSid)) {
+ Error = GetLastError();
+ goto cleanup;
+ }
+
+ pcSubAuthorityCount = GetSidSubAuthorityCount( *ppsidAccountSid );
+ RPL_ASSERT( pcSubAuthorityCount != NULL );
+ (*pcSubAuthorityCount)++;
+ pdwLastSubAuthority = GetSidSubAuthority(
+ *ppsidAccountSid, (DWORD)((*pcSubAuthorityCount)-1) );
+ RPL_ASSERT( pdwLastSubAuthority != NULL );
+ *pdwLastSubAuthority = ulRid;
+
+cleanup:
+
+ if ( Error != NO_ERROR) {
+ if ( *ppsidAccountSid != NULL) {
+ RplMemFree( RG_MemoryHandle, *ppsidAccountSid);
+ }
+ }
+
+ return( Error);
+}
+
+
+SID_IDENTIFIER_AUTHORITY IDAuthorityNT = SECURITY_NT_AUTHORITY;
+
+DWORD InitSdBlock( OUT RPL_SD_BLOCK * psdblock)
+/*++
+ Initialize SdBlock.
+--*/
+{
+ DWORD Error;
+ DWORD cbNewDacl;
+
+ memset( psdblock, 0, sizeof(*psdblock));
+
+ //
+ // Get System SID
+ //
+ Error = RtlAllocateAndInitializeSid( &IDAuthorityNT, 1,
+ SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
+ &(psdblock->psidSystem));
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+
+ //
+ // Get Administrators SID
+ //
+ Error = RtlAllocateAndInitializeSid(
+ &IDAuthorityNT,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &(psdblock->psidAdministrators)
+ );
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+
+ //
+ // Get information about local and (if not PDC) remote domain
+ //
+ Error = GetDomains( &(psdblock->hsamWkstaAccountDomain),
+ &(psdblock->pinfoWkstaAccountDomain),
+ &(psdblock->hsamRPLUSERAccountDomain),
+ &(psdblock->pinfoRPLUSERAccountDomain),
+ GENERIC_EXECUTE );
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Start assembling DACL
+ //
+ cbNewDacl = sizeof(ACL) + (3*sizeof(ACCESS_ALLOWED_ACE))
+ + (3*GetSidLengthRequired(SID_MAX_SUB_AUTHORITIES))
+ - sizeof(DWORD);
+ psdblock->paclDacl = RplMemAlloc( RG_MemoryHandle, cbNewDacl);
+ if ( psdblock->paclDacl == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ return( Error);
+ }
+ if ( !InitializeAcl( psdblock->paclDacl, cbNewDacl, ACL_REVISION)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Add System ACE
+ //
+ Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidSystem);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Add Administrators ACE
+ //
+ Error = AddInheritableAce( psdblock->paclDacl, GENERIC_ALL, psdblock->psidAdministrators);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Build security descriptor
+ //
+ psdblock->psd = RplMemAlloc( RG_MemoryHandle, sizeof(SECURITY_DESCRIPTOR) );
+ if ( psdblock->psd == NULL) {
+ Error = ERROR_NOT_ENOUGH_MEMORY;
+ return( Error);
+ }
+ if ( !InitializeSecurityDescriptor( psdblock->psd,
+ SECURITY_DESCRIPTOR_REVISION )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ if ( !SetSecurityDescriptorDacl( psdblock->psd, TRUE, psdblock->paclDacl, FALSE)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ //
+ // Set SecurityType in RPL_SD_BLOCK
+ //
+ psdblock->MonitorSecurityType = TRUE;
+ psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ return( NO_ERROR);
+}
+
+
+DWORD UpdateSdBlock(
+ IN OUT RPL_SD_BLOCK * psdblock,
+ IN RPL_SD_BLOCK_TYPE SecurityType,
+ IN DWORD GrantToRid,
+ IN BOOL MonitorSecurityType
+ )
+/*--
+ Updates the changeable ace in the security descriptor
+ to reflect GrantToRid.
+
+ MonitorSecurityType - do we need to monitor (keep evaluating) security type
+ while working on this subtree
+--*/
+{
+ DWORD Error = NO_ERROR;
+ PSID pGrantToSid = NULL;
+
+ //
+ // Delete the changeable ACE if present
+ //
+ if ( psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ONEUSER
+ || psdblock->SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS) {
+ if ( !DeleteAce( psdblock->paclDacl, RPL_INDEX_CHANGEABLE_ACE)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ psdblock->SecurityType = RPL_SD_BLOCK_TYPE_ADMINONLY;
+ }
+
+ //
+ // Rewrite the changeable ACE if needed
+ //
+ if ( GrantToRid != 0) {
+ Error = MyBuildSid( &pGrantToSid,
+ (SecurityType == RPL_SD_BLOCK_TYPE_ALLUSERS)
+ ? psdblock->pinfoRPLUSERAccountDomain
+ : psdblock->pinfoWkstaAccountDomain,
+ GrantToRid);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ switch( SecurityType) {
+ case RPL_SD_BLOCK_TYPE_ALLUSERS:
+ Error = AddInheritableAce( psdblock->paclDacl,
+ GENERIC_READ | GENERIC_EXECUTE,
+ pGrantToSid);
+ break;
+ case RPL_SD_BLOCK_TYPE_ONEUSER:
+ Error = AddInheritableAce( psdblock->paclDacl,
+ GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE,
+ pGrantToSid);
+ break;
+ default:
+ RPL_ASSERT( FALSE);
+ Error = NERR_RplInternal;
+ break;
+ }
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ psdblock->SecurityType = SecurityType;
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ psdblock->MonitorSecurityType = MonitorSecurityType;
+ }
+ if ( pGrantToSid != NULL) {
+ RplMemFree( RG_MemoryHandle, pGrantToSid);
+ }
+ return( Error);
+}
+
+
+
+RPL_SD_BLOCK_TYPE SecurityType( IN PWCHAR File)
+/*++
+ Based on a position of File in %RplRoot% tree, determines what
+ SECURITY_DESCRIPTOR type is appropriate for this file.
+--*/
+{
+#define WCSLEN( _x_) ( (DWORD) (sizeof(_x_)/sizeof(WCHAR) - 1) )
+#define SEC_RPLFILES L"RPLFILES\\"
+#define SEC_BINFILES L"BINFILES"
+#define SEC_PROFILES L"PROFILES"
+#define SEC_MACHINES L"MACHINES\\"
+#define SEC_TMPFILES L"TMPFILES\\"
+
+ RPL_ASSERT( _wcsnicmp( File, RG_Directory, RG_DirectoryLength) == 0);
+
+ File += RG_DirectoryLength;
+
+ //
+ // If file is not in rplfiles, then it should be accessible to
+ // admins only.
+ //
+ if ( _wcsnicmp( File, SEC_RPLFILES, WCSLEN( SEC_RPLFILES)) != 0) {
+ return( RPL_SD_BLOCK_TYPE_ADMINONLY);
+ }
+ File += WCSLEN( SEC_RPLFILES);
+
+ //
+ // If file is in rplfiles\binfiles or rplfiles\profiles, then it
+ // should be accessible to admins & RPLUSER group.
+ //
+
+ if ( _wcsnicmp( File, SEC_BINFILES, WCSLEN( SEC_BINFILES)) == 0 ||
+ _wcsnicmp( File, SEC_PROFILES, WCSLEN( SEC_PROFILES)) == 0) {
+ return( RPL_SD_BLOCK_TYPE_ALLUSERS);
+ }
+
+ //
+ // If file is in rplfiles\machines\* or rplfiles\tmpfiles\*,
+ // then it should be accessible to admins & a particular workstation
+ // account.
+ //
+ if ( _wcsnicmp( File, SEC_MACHINES, WCSLEN( SEC_MACHINES)) == 0 ||
+ _wcsnicmp( File, SEC_TMPFILES, WCSLEN( SEC_TMPFILES)) == 0) {
+ return( RPL_SD_BLOCK_TYPE_ONEUSER);
+ }
+
+ //
+ // Return the default value. Files in rplfiles\configs will take
+ // this code path.
+ //
+ return( RPL_SD_BLOCK_TYPE_ADMINONLY);
+}
+
+
+DWORD SetRplPerms(
+ IN PWCHAR File,
+ IN OUT PBOOL pAuxiliaryBlock
+ )
+/*--
+ This is the callback routine called by RplDoTree to set permissions
+ on a specific file/directory.
+
+ File Path to file or directory.
+ pAuxiliaryBlock Pointer to RPL_SD_BLOCK the first element
+ of which must be boolean SkipThisTree
+
+--*/
+{
+ PRPL_SD_BLOCK psdblock = (PRPL_SD_BLOCK)pAuxiliaryBlock;
+
+ psdblock->SkipThisTree = FALSE; // by default we do not skip
+
+ if ( psdblock->MonitorSecurityType) {
+ //
+ // We skip subtrees that require different security information.
+ //
+ if ( SecurityType( File) != psdblock->SecurityType) {
+ psdblock->SkipThisTree = TRUE;
+ return( NO_ERROR);
+ }
+ }
+
+ //
+ // Set the actual file security. Note that this call will succeed
+ // even if the volume is not NTFS, thus we must check for
+ // partition type elsewhere.
+ //
+ if ( !SetFileSecurity( File, DACL_SECURITY_INFORMATION, psdblock->psd)) {
+ DWORD Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ }
+
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetSecurity(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD WkstaRid,
+ IN DWORD RplUserRid
+ )
+/*++
+ There are 3 types of permissions: ADMINONLY, RPLUSER & ONEUSER.
+ For each type we first construct the appropriate security
+ description then use it.
+--*/
+{
+ DWORD Error;
+ WCHAR Directory[ MAX_PATH];
+ BOOL IsNtfs;
+ RPL_SD_BLOCK sdblock;
+
+ //
+ // Make working copy of RG_Directory, then verify it is NTFS.
+ // If it is not NTFS then there is nothing for us to do here.
+ //
+
+ wcscpy( Directory, RG_Directory);
+ Error = IsNTFS( Directory, &IsNtfs);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ return( Error);
+ } else if (!IsNtfs) {
+ return( NO_ERROR);
+ }
+
+ //
+ // Initialize to ADMINONLY permissions only.
+ //
+ Error = InitSdBlock( &sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // If RplUserRid is given, set both ADMINONLY & RPLUSER permissions
+ // (different permissions on different trees).
+ //
+ if ( RplUserRid != 0) {
+ //
+ // Set ADMINONLY permissions.
+ //
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ //
+ // Initialize to RPLUSER permissions, then set them.
+ //
+ Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ALLUSERS, RplUserRid, FALSE);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\BINFILES");
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\PROFILES");
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ //
+ // If WkstaRid is given, initilize then set ONEUSER permissions.
+ //
+ if ( WkstaRid != 0) {
+ if ( !ValidName( WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ Error = UpdateSdBlock( &sdblock, RPL_SD_BLOCK_TYPE_ONEUSER, WkstaRid, FALSE);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\MACHINES\\");
+ wcscat( Directory, WkstaName);
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ wcscpy( Directory, RG_Directory);
+ wcscat( Directory, L"RPLFILES\\TMPFILES\\");
+ wcscat( Directory, WkstaName);
+ Error = RplDoTree( Directory, NULL, RPL_TREE_AUXILIARY,
+ SetRplPerms, (PVOID)&sdblock);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ DeInitSdBlock( &sdblock);
+ if (Error != NO_ERROR) {
+ //
+ // CODEWORK Need a more appropriate event log ?!
+ //
+ RplReportEvent( NELOG_RplCheckSecurity, NULL, sizeof(DWORD), &Error);
+ }
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/service.c b/private/net/svcdlls/rpl/server/service.c
new file mode 100644
index 000000000..d18e0778b
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/service.c
@@ -0,0 +1,207 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ service.c
+
+Abstract:
+
+ This module contains RPL service apis: Open, Close, GetInfo & SetInfo.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "apisec.h"
+#include <secobj.h> // NetpAccessCheckAndAuditAlarm()
+#include "db.h" // ResumePrune()
+#include "rplsvc_s.h" // RPL_RPC_HANDLE_rundown()
+#include "setup.h"
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplOpen(
+ IN PWCHAR ServerName,
+ OUT LPRPL_RPC_HANDLE pServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+Return Value:
+
+ NO_ERROR if success.
+
+--*/
+{
+ //
+ // ServerName got us to the server side and is uninteresting
+ // once we get here.
+ //
+ UNREFERENCED_PARAMETER( ServerName);
+
+#ifndef RPL_NO_SERVICE
+ //
+ // Perform access validation on the caller. All other
+ // operations are handle based, thus we can rely on RPC
+ // to prohibit unathorized callers.
+ //
+ if ( NetpAccessCheckAndAudit(
+ SERVICE_RIPL, // Subsystem name
+ SECURITY_OBJECT, // Object type name
+ RG_SecurityDescriptor, // Security descriptor
+ RPL_RECORD_ALL_ACCESS, // Desired access
+ &RG_SecurityMapping // Generic mapping
+ ) != NO_ERROR) {
+ return ERROR_ACCESS_DENIED;
+ }
+#endif
+
+ EnterCriticalSection( &RG_ProtectServerHandle);
+ *(PDWORD)pServerHandle = ++RG_ServerHandle;
+ LeaveCriticalSection( &RG_ProtectServerHandle);
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplClose(
+ IN OUT LPRPL_RPC_HANDLE pServerHandle
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+Return Value:
+
+ NO_ERROR if success.
+
+--*/
+{
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ ResumePrune( pSession, *(PDWORD)pServerHandle);
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ *(PDWORD)pServerHandle = 0; // let rpc know that we are done
+ return( ERROR_SUCCESS);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplGetInfo(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_INFO_STRUCT InfoStruct
+ )
+{
+ LPBYTE Buffer;
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_INFO_0));
+ InfoStruct->RplInfo0 = (LPRPL_INFO_0)Buffer;
+ InfoStruct->RplInfo0->Flags = 0;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_INFO_1));
+ InfoStruct->RplInfo1 = (LPRPL_INFO_1)Buffer;
+ InfoStruct->RplInfo1->AdapterPolicy = 0; // fix i_lmrpl.h
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplSetInfo(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_INFO_STRUCT InfoStruct,
+ IN OUT LPDWORD ErrorParameter
+ )
+{
+ DWORD Flags;
+ DWORD Error;
+
+ switch( Level) {
+ case 0:
+ Flags = InfoStruct->RplInfo0->Flags;
+ break;
+ case 1:
+ Flags = InfoStruct->RplInfo1->AdapterPolicy; // fix i_lmrpl.h
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( Flags & ~RPL_SPECIAL_ACTIONS) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+#ifdef RPL_NO_SERVICE
+ if ( Flags == 0) {
+ RplControlHandler( SERVICE_CONTROL_STOP);
+ }
+#endif
+ Error = SetupAction( &Flags, TRUE); // full backup if any
+ return( Error);
+}
+
+
+VOID
+RPL_RPC_HANDLE_rundown(
+ IN RPL_RPC_HANDLE ServerHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by RPC when a connection is broken that had
+ an outstanding context handle. The value of the context handle is
+ passed in here so that we have an opportunity to clean up.
+
+Arguments:
+
+ ServerHandle - This is the handle value of the context handle that is broken.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ NetrRplClose( &ServerHandle); // close the handle
+}
+
diff --git a/private/net/svcdlls/rpl/server/setup.c b/private/net/svcdlls/rpl/server/setup.c
new file mode 100644
index 000000000..36ee21738
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/setup.c
@@ -0,0 +1,716 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ setup.c
+
+Abstract:
+
+ Setup primitives
+
+Author:
+
+ Jon Newman (jonn) 01-April-1994
+
+Revision History:
+
+ Jon Newman (jonn) 01 - April - 1994
+ Added RplPolicy primitives
+
+--*/
+
+
+#include "local.h"
+#include "rpldb.h" // Call(), CallM()
+#include "db.h" // ConfigGetField()
+#include "dblib.h" // RplFilterFirst()
+#include "wksta.h" // WKSTA_WkstaName
+#include "profile.h" // PROFILE_ProfileName
+#include "setup.h"
+#include "config.h"
+#include "lmwksta.h" // NetWkstaGetInfo
+
+#define RPLDISK_SYS L"BBLOCK\\RPLDISK.SYS"
+#define RPLDISK_OLD L"BBLOCK\\RPLDISK.OLD"
+#define RPLDISK_NEW L"RPLDISK.NEW"
+
+
+
+BOOL FileExists( const WCHAR * FileToFind, DWORD * ErrorPtr)
+/*++
+
+Routine Description:
+
+ This function checks whether the file exists.
+
+Arguments:
+
+
+Return Value:
+ TRUE iff file exists
+
+--*/
+{
+ BOOL BoolFileFound = FALSE;
+ DWORD TempError;
+
+ if ( ErrorPtr == NULL )
+ ErrorPtr = &TempError;
+
+ *ErrorPtr = NO_ERROR;
+
+ if (0xFFFFFFFF == GetFileAttributes( FileToFind )) {
+ *ErrorPtr = GetLastError();
+ switch (*ErrorPtr) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ break;
+ case NO_ERROR:
+ default:
+ RplDump( ++RG_Assert, ( "*ErrorPtr=%d", *ErrorPtr));
+ break;
+ }
+ } else {
+ BoolFileFound = TRUE;
+ }
+
+ return( BoolFileFound);
+}
+
+
+DWORD RplReplaceRPLDISK( VOID)
+/*++
+
+Routine Description:
+
+ This function checks whether the file RPLDISK.SYS has
+ been replaced with the newer version, and if not,
+ replaces it.
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+ WCHAR RpldiskBuffer[ MAX_PATH ];
+ WCHAR SourceBuffer[ MAX_PATH ];
+ WCHAR BackupBuffer[ MAX_PATH ];
+
+ RPL_ASSERT( RG_DirectoryLength == wcslen(RG_Directory)
+ && RG_DirectoryLength != 0
+ && (RG_DirectoryLength + wcslen(RPLDISK_OLD)) < MAX_PATH );
+
+ wcscpy( RpldiskBuffer, RG_Directory );
+ wcscat( RpldiskBuffer, RPLDISK_SYS );
+
+ wcscpy( SourceBuffer, RG_Directory );
+ wcscat( SourceBuffer, RPLDISK_NEW );
+
+ //
+ // Make sure source file exists
+ //
+
+ if (!FileExists(SourceBuffer, &Error)) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ wcscpy( BackupBuffer, RG_Directory );
+ wcscat( BackupBuffer, RPLDISK_OLD );
+
+ //
+ // Copy existing RPLDISK.SYS to RPLDISK.OLD, on a best-effort basis
+ //
+ (void) CopyFile( RpldiskBuffer, BackupBuffer, FALSE );
+
+ //
+ // Copy the new RPLDISK.SYS
+ //
+ if (!CopyFile( SourceBuffer, RpldiskBuffer, FALSE )) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+cleanup:
+
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplReplaceRPLDISK, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+#define LANMANINI_BINFILES L"RPLFILES\\BINFILES\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_CONFIGSDOS L"RPLFILES\\CONFIGS\\DOS\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_PROFILES L"RPLFILES\\PROFILES\\%ws\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_WKSTAS L"RPLFILES\\MACHINES\\%ws\\%ws\\PRO\\LANMAN.DOS\\LANMAN.INI"
+#define LANMANINI_SECTION L"WORKSTATION"
+#define LANMANINI_KEY L"DOMAIN"
+
+
+DWORD RplProcessLanmanInis( VOID )
+/*++
+
+Routine Description:
+
+ This function changes the "domain =" line in every LANMAN.INI
+
+Arguments:
+
+Return Value:
+ error code
+
+--*/
+{
+ DWORD Error;
+ WCHAR achFile[ MAX_PATH ];
+ WKSTA_INFO_100 * pw100 = NULL;
+ WCHAR * pwszDomainName = NULL;
+ PRPL_SESSION pSession = &RG_ApiSession;
+ BOOL InCritSec = FALSE;
+ BOOL InEnumeration = FALSE;
+ INT SpaceLeft;
+ BOOL TableEnd = TRUE;
+ WCHAR * EnumProfileName = NULL;
+ WCHAR * EnumWkstaName = NULL;
+ DWORD EnumWkstaFlags = 0;
+
+ RPL_ASSERT( wcslen(RG_Directory) == RG_DirectoryLength
+ && RG_DirectoryLength+wcslen(LANMANINI_CONFIGSDOS) < MAX_PATH
+ && RG_DirectoryLength+wcslen(LANMANINI_BINFILES ) < MAX_PATH );
+
+ //
+ // get domain name
+ //
+ // CODEWORK OK for workgroup? Non-issue since we should be on NTAS
+ //
+ Error = NetWkstaGetInfo( NULL, 100, (LPBYTE *)&pw100);
+ if (Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ RPL_ASSERT( pw100 != NULL );
+ pwszDomainName = pw100->wki100_langroup;
+ RPL_ASSERT( pwszDomainName != NULL && *pwszDomainName != L'\0' );
+
+ //
+ // update rplfiles\configs\dos\lanman.dos\lanman.ini
+ //
+ wcscpy( achFile, RG_Directory );
+ wcscat( achFile, LANMANINI_CONFIGSDOS );
+
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // update rplfiles\binfiles\lanman.dos\lanman.ini
+ //
+ wcscpy( achFile + RG_DirectoryLength, LANMANINI_BINFILES );
+
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // update rplfiles\profiles\<profilename>\lanman.dos\lanman.ini
+ //
+ // Code lifted from NetrRplConfigEnum
+ //
+ EnterCriticalSection( &RG_ProtectDatabase);
+ InCritSec = TRUE;
+ Call( JetBeginTransaction( pSession->SesId));
+ InEnumeration = TRUE;
+
+ if ( !RplFilterFirst( pSession, PROFILE_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+
+ for ( ; ; ) {
+
+ if ( TableEnd == TRUE) {
+ break;
+ }
+
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumProfileName = NULL;
+ }
+
+ // get profile name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = ProfileGetField( pSession,
+ PROFILE_ProfileName,
+ &EnumProfileName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // build file name and modify file
+ if ( ( RG_DirectoryLength
+ + wcslen(LANMANINI_PROFILES)
+ + wcslen(EnumProfileName)
+ - 3) >= MAX_PATH )
+ {
+ RplDump( ++RG_Assert, ( "wkstaname too long \"%ws\"", EnumWkstaName));
+ }
+ else
+ {
+ swprintf( achFile + RG_DirectoryLength,
+ LANMANINI_PROFILES,
+ EnumProfileName );
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+ if ( !RplFilterNext( pSession, pSession->ProfileTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ InEnumeration = FALSE;
+
+ //
+ // update rplfiles\machines\<wkstaname>\<profilename>\lanman.dos\lanman.ini
+ //
+ Call( JetBeginTransaction( pSession->SesId));
+ InEnumeration = TRUE;
+ if ( !RplFilterFirst( pSession, WKSTA_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ for ( ; ; ) {
+
+ if ( TableEnd == TRUE) {
+ break;
+ }
+
+ // check whether this wksta has a personal profile
+ SpaceLeft = sizeof(DWORD);
+ Error = WkstaGetField( pSession,
+ WKSTA_Flags,
+ (LPVOID *)&EnumWkstaFlags,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // only write for personal profile
+ if ( EnumWkstaFlags & WKSTA_FLAGS_SHARING_FALSE ) {
+
+ if (EnumWkstaName != NULL) {
+ MIDL_user_free( EnumWkstaName );
+ EnumProfileName = NULL;
+ }
+
+ // get workstation name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = WkstaGetField( pSession,
+ WKSTA_WkstaName,
+ &EnumWkstaName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumProfileName = NULL;
+ }
+
+ // get workstation-in-profile name
+ SpaceLeft = MAX_PATH; // CODEWORK * sizeof(WCHAR)?
+ Error = WkstaGetField( pSession,
+ WKSTA_ProfileName,
+ &EnumProfileName,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ // build file name and modify file
+ if ( ( RG_DirectoryLength
+ + wcslen(LANMANINI_WKSTAS)
+ + wcslen(EnumWkstaName)
+ + wcslen(EnumProfileName)
+ - 6) >= MAX_PATH )
+ {
+ RplDump( ++RG_Assert,
+ ( "Wksta+profile too long \"%ws\", \"%ws\"",
+ EnumWkstaName,
+ EnumProfileName));
+ }
+ else
+ {
+ swprintf( achFile + RG_DirectoryLength,
+ LANMANINI_WKSTAS,
+ EnumWkstaName,
+ EnumProfileName );
+ if ( !WritePrivateProfileString( LANMANINI_SECTION,
+ LANMANINI_KEY,
+ pwszDomainName,
+ achFile ))
+ {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+ }
+
+
+ }
+
+
+ if ( !RplFilterNext( pSession, pSession->WkstaTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+
+ if ( InEnumeration) {
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ }
+
+ if ( InCritSec) {
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ }
+ if (EnumProfileName != NULL) {
+ MIDL_user_free( EnumProfileName );
+ EnumWkstaName = NULL;
+ }
+
+ if (EnumWkstaName != NULL) {
+ MIDL_user_free( EnumWkstaName );
+ EnumWkstaName = NULL;
+ }
+
+ if ( pw100 != NULL) {
+ NetApiBufferFree( pw100);
+ }
+
+ //
+ // BUGBUG Need better event id/text?
+ //
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplCheckSecurity, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+#define RPL_COM L"\\COMMAND.COM"
+#define WCSLEN( _x_) ((DWORD) ( sizeof(_x_)/sizeof(WCHAR) - 1))
+
+BOOL RplConfigEnabled( IN PWCHAR DirName2)
+{
+ WCHAR SearchBuffer[ MAX_PATH ];
+ DWORD SearchBufferLength;
+
+ wcscpy( SearchBuffer, RG_DiskBinfiles);
+ SearchBufferLength = wcslen( SearchBuffer);
+
+ if ( SearchBufferLength + wcslen(DirName2) + WCSLEN( RPL_COM)
+ > WCSLEN( SearchBuffer)) {
+ RplDump( ++RG_Assert, ( "DirName2=%ws", DirName2));
+ return( FALSE);
+ }
+ wcscpy( SearchBuffer + SearchBufferLength, DirName2);
+ wcscat( SearchBuffer + SearchBufferLength, RPL_COM);
+ /*
+ * The file COMMAND.COM should be in directory given by SearchBuffer
+ */
+ return( FileExists( SearchBuffer, NULL));
+}
+
+
+DWORD RplCheckConfigs( VOID)
+/*++
+
+Routine Description:
+
+ This function checks whether the file COMMAND.COM exists
+ in the proper place for each configuration, and
+ enables/disables the configuration accordingly.
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ DWORD Error = NO_ERROR;
+ INT SpaceLeft;
+ BOOL TableEnd;
+ WCHAR * DirName2 = NULL;
+ DWORD Flags;
+ WCHAR SearchBuffer[ MAX_PATH ];
+ BOOL BoolConfigEnabled = FALSE;
+ BOOL BoolFileFound = FALSE;
+ DWORD SearchBufferLength;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ wcscpy( SearchBuffer, RG_DiskBinfiles);
+ SearchBufferLength = wcslen( SearchBuffer);
+
+ //
+ // Code lifted from NetrRplConfigEnum
+ //
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, CONFIG_TABLE_TAG, NULL, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+
+ if (DirName2 != NULL) {
+ MIDL_user_free( DirName2 );
+ DirName2 = NULL;
+ }
+
+ Error = ConfigGetField( pSession,
+ CONFIG_DirName2,
+ &DirName2,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ Error = ConfigGetField( pSession,
+ CONFIG_Flags,
+ (LPVOID *)&Flags,
+ &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ goto cleanup;
+ }
+
+ //
+ // Check whether this configuration should be enabled
+ //
+ RPL_ASSERT( (!!(Flags & CONFIG_FLAGS_ENABLED_TRUE))
+ != (!!(Flags & CONFIG_FLAGS_ENABLED_FALSE)) );
+ BoolConfigEnabled = !!(Flags & CONFIG_FLAGS_ENABLED_TRUE);
+
+ if ( SearchBufferLength + wcslen(DirName2) + WCSLEN( RPL_COM)
+ > WCSLEN( SearchBuffer)) {
+ RplDump( ++RG_Assert, ( "DirName2=%ws", DirName2));
+ continue;
+ }
+ wcscpy( SearchBuffer + SearchBufferLength, DirName2);
+ wcscat( SearchBuffer + SearchBufferLength, RPL_COM);
+ /*
+ * The file COMMAND.COM should be in directory given by SearchBuffer
+ */
+ BoolFileFound = FileExists( SearchBuffer, NULL );
+
+ if (Error == NERR_Success && BoolConfigEnabled != BoolFileFound) {
+ Flags ^= CONFIG_FLAGS_MASK_ENABLED; // XOR
+
+ // lifted from NetrRplConfigAdd and NetrWkstaSetinfo
+ CallJ( JetPrepareUpdate( pSession->SesId,
+ pSession->ConfigTableId,
+ JET_prepReplace));
+
+ // lifted from ConfigSetInfo
+ CallM( JetSetColumn( pSession->SesId, pSession->ConfigTableId,
+ ConfigTable[ CONFIG_Flags].ColumnId,
+ &Flags, sizeof( Flags), 0, NULL));
+
+ // lifted from NetrRplConfigAdd
+ CallJ( JetUpdate( pSession->SesId,
+ pSession->ConfigTableId,
+ NULL, 0, NULL));
+ }
+
+ if ( !RplFilterNext( pSession, pSession->ConfigTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0)); // CODEWORK rollback on error?
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if (DirName2 != NULL) {
+ MIDL_user_free( DirName2 );
+ DirName2 = NULL;
+ }
+
+ if (Error != NO_ERROR) {
+ RplReportEvent( NELOG_RplCheckConfigs, NULL, sizeof(DWORD), &Error );
+ }
+
+ return( Error);
+}
+
+
+DWORD RplBackupDatabase( IN BOOL FullBackup)
+/*++
+
+Routine Description:
+
+ This function backs up the JET database. FullBackup copies the
+ database file and all log files. Incremental backup copies only
+ the log files that are modified since the last backup.
+
+Arguments:
+
+ FullBackup - set to TRUE if full backup is required.
+
+Return Value:
+
+ Windows Error.
+
+--*/
+{
+ JET_ERR JetError;
+
+ FullBackup = TRUE; // to avoid JET bugs in JetRestore()
+
+ JetError = JetBackup( RG_BackupPath,
+#ifdef __JET500
+ JET_bitBackupAtomic |
+ (FullBackup ? 0
+ : JET_bitBackupIncremental),
+ NULL
+#else
+ (FullBackup ? JET_bitOverwriteExisting
+ : JET_bitBackupIncremental)
+#endif
+ );
+
+ if( JetError == JET_errFileNotFound && FullBackup == FALSE) {
+
+ //
+ // Full backup was not performed anytime, so do it now.
+ //
+
+ JetError = JetBackup( RG_BackupPath,
+#ifdef __JET500
+ JET_bitBackupAtomic,
+ NULL
+#else
+ JET_bitOverwriteExisting
+#endif
+ );
+ }
+
+ if ( JetError < 0) {
+ RplReportEvent( NELOG_RplBackupDatabase, NULL, sizeof(DWORD), &JetError);
+ return( NERR_RplBackupDatabase);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD SetupAction(
+ IN OUT PDWORD pAction,
+ IN BOOL FullBackup
+ )
+/*++
+
+Routine Description:
+
+ Performs the setup action for all RPL_SPECIAL_ACTIONS specified
+ in *pAction.
+
+Arguments:
+ pAction: points to RPL_SPECIAL_ACTIONS DWORD. On return, the
+ RPL_SPECIAL_ACTIONS flags are reset for all actions which
+ were successfully performed.
+
+Return Value:
+ error word
+
+--*/
+{
+ DWORD Error;
+
+ if ((*pAction) & RPL_REPLACE_RPLDISK) {
+#ifdef NOT_YET
+ Error = RplReplaceRPLDISK();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+#endif
+ *pAction &= ~RPL_REPLACE_RPLDISK;
+ }
+
+ if ((*pAction) & RPL_CHECK_SECURITY) {
+ Error = RplProcessLanmanInis();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_CHECK_SECURITY;
+ }
+
+ if ((*pAction) & RPL_CHECK_CONFIGS) {
+ Error = RplCheckConfigs();
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_CHECK_CONFIGS;
+ }
+
+ if ((*pAction) & RPL_CREATE_PROFILES) {
+ //
+ // CODEWORK RPL_CREATE_PROFILES not implemented
+ //
+ *pAction &= ~RPL_CREATE_PROFILES;
+ }
+
+ if ((*pAction) & RPL_BACKUP_DATABASE) {
+ Error = RplBackupDatabase( FullBackup);
+ if (Error != NO_ERROR) {
+ return( Error);
+ }
+ *pAction &= ~RPL_BACKUP_DATABASE;
+ }
+
+ return( NO_ERROR);
+}
+
diff --git a/private/net/svcdlls/rpl/server/setup.h b/private/net/svcdlls/rpl/server/setup.h
new file mode 100644
index 000000000..67993c662
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/setup.h
@@ -0,0 +1,34 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ setup.h
+
+Abstract:
+
+ Exports from setup.c
+
+Author:
+
+ Jon Newman (jonn) 01-April-1994
+
+Revision History:
+
+ Jon Newman (jonn) 01 - April - 1994
+ Added setup primitives
+
+--*/
+
+#ifndef _RPL_SETUP_H_
+#define _RPL_SETUP_H_
+
+DWORD SetupAction(
+ IN OUT PDWORD pAction,
+ IN BOOL FullBackup
+ );
+DWORD RplBackupDatabase( IN BOOL FullBackup);
+BOOL RplConfigEnabled( IN PWCHAR DirName2);
+
+#endif // _RPL_SETUP_H_
diff --git a/private/net/svcdlls/rpl/server/sources b/private/net/svcdlls/rpl/server/sources
new file mode 100644
index 000000000..537309ea0
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/sources
@@ -0,0 +1,83 @@
+#
+# Source file used for building RplSvc.exe in this directory.
+#
+
+MAJORCOMP=net
+MINORCOMP=rplsvc
+
+TARGETPATH=obj
+TARGETNAME=rplsvc
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ ..\dll\obj\*\rplnet.lib \
+ ..\lib\obj\*\rpllib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\dlcapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\wsock32.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+INCLUDES=.;..\inc;..\..\..\inc;..\..\..\api;..\..\..\..\inc;
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ apisec.c \
+ dblib.c \
+ tree.c \
+ vendor.c \
+ security.c \
+ adapter.c \
+ disk.c \
+ service.c \
+ resume.c \
+ boot.c \
+ config.c \
+ profile.c \
+ setup.c \
+ wksta.c \
+ fitfile.c \
+ open.c \
+ bbcfile.c \
+ read.c \
+ database.c \
+ request.c \
+ worker.c \
+ rplsvc.c \
+ rplsvc.rc \
+ rplmain.c \
+ debug.c \
+ report.c \
+ library.c \
+ memory.c \
+ rplsvc_s.c
+
+USE_CRTDLL=1
+#
+#Add -DRPL_NO_SERVICE if you do not want RPL to run as a service
+#
+#C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32 -DRPL_NO_SERVICE
+#
+
+C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DWIN32
+
+UMTYPE=console
+UMTEST= rplrest
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\jet500.lib \
+ $(BASEDIR)\public\sdk\lib\*\netlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+
diff --git a/private/net/svcdlls/rpl/server/tree.c b/private/net/svcdlls/rpl/server/tree.c
new file mode 100644
index 000000000..40ede9642
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/tree.c
@@ -0,0 +1,353 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.c
+
+Abstract:
+
+ RplTreeCopy, RplTreeDelete, RplMakeDir file-management helper routines
+ for the RPL service.
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+ Jon Newman (jonn) 15 - February - 1994
+ Added RplGrant*Perms primitives
+ Jon Newman (jonn) 08 - March - 1994
+ RplDoTree now creates log entries on failure
+
+--*/
+
+#include "local.h"
+#include "tree.h"
+#include "treei.h"
+
+#define FILE_SEPARATOR L"\\"
+#define HERE_DIRECTORY L"."
+#define PARENT_DIRECTORY L".."
+#define ALLFILES_SUFFIX L"*"
+
+#define LEN_FILE_SEPARATOR 1
+#define LEN_ALLFILES_SUFFIX 1
+
+
+VOID LoadError(
+ PWCHAR FilePath,
+ DWORD ActionError,
+ DWORD ActionFlags
+ )
+/*++
+ BUGBUG if buffer inadequate, copy first part or last part?
+--*/
+{
+ DWORD Length0 = 0;
+ DWORD msgid = 0;
+
+ //
+ // Dump error log entry, where the message depends on Flags
+ //
+ if (ActionFlags == 0) {
+ msgid = 0;
+ } else if (ActionFlags & RPL_TREE_COPY) {
+ msgid = NELOG_RplFileCopy;
+ } else if (ActionFlags & RPL_TREE_DELETE) {
+ msgid = NELOG_RplFileDelete;
+ } else if (ActionFlags & RPL_TREE_AUXILIARY) {
+ msgid = NELOG_RplFilePerms;
+ } else {
+ RPL_ASSERT( FALSE );
+ }
+
+ if (msgid != 0) {
+ RplReportEvent( msgid, FilePath, sizeof(DWORD), &ActionError );
+ }
+}
+
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PBOOL pAuxiliaryBlock
+ )
+/*++
+ Target should only be examined if RPL_TREE_COPY case.
+--*/
+{
+ // CODEWORK too much stack space usage (1.5K / recursive invocation)
+ DWORD Error = NO_ERROR;
+ WCHAR CurrentSource[ MAX_PATH];
+ WCHAR CurrentTarget[ MAX_PATH];
+ HANDLE Handle = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA FindData;
+ DWORD SourceLength = 0;
+ DWORD TargetLength = 0;
+ DWORD FileLength = 0;
+ BOOL Auxiliary = ((Flags & RPL_TREE_AUXILIARY) != 0);
+ BOOL Delete = ((Flags & RPL_TREE_DELETE) != 0);
+ BOOL Copy = ((Flags & RPL_TREE_COPY) != 0);
+
+ //
+ // Delete is incompatible with Auxiliary and Copy
+ //
+ RPL_ASSERT( !Delete || (!Auxiliary && !Copy ) );
+
+ //
+ // Callback and callback data block required for Auxiliary
+ //
+ RPL_ASSERT( !Auxiliary
+ || (AuxiliaryCallback != NULL && pAuxiliaryBlock != NULL) );
+
+ if ( Source == NULL || *Source == L'\0') {
+ RPL_RETURN( ERROR_INVALID_PARAMETER);
+ }
+ SourceLength = wcslen( Source);
+ if ( SourceLength + LEN_ALLFILES_SUFFIX >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, Flags);
+ return( Error);
+ }
+ //
+ // Initialize buffer for source names.
+ //
+ memcpy( CurrentSource, Source, SourceLength * sizeof(WCHAR));
+ memcpy( CurrentSource + SourceLength, FILE_SEPARATOR, LEN_FILE_SEPARATOR * sizeof(WCHAR));
+ memcpy( CurrentSource + SourceLength + LEN_FILE_SEPARATOR, ALLFILES_SUFFIX, (LEN_ALLFILES_SUFFIX+1) * sizeof(WCHAR));
+
+ //
+ // Copy directory operations are done at the beginning.
+ //
+ // BUGBUG broken if source is not a directory
+ //
+ if ( Copy) {
+ TargetLength = wcslen( Target);
+ RPL_ASSERT( TargetLength < MAX_PATH);
+ //
+ // Initialize buffer for target names.
+ //
+ memcpy( CurrentTarget, Target, (TargetLength+1) * sizeof(WCHAR));
+ if ( !CreateDirectoryEx( Source, Target, NULL)) {
+ Error = GetLastError();
+ if ( Error != ERROR_ALREADY_EXISTS) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_COPY);
+ return( Error);
+ }
+ }
+ }
+
+ //
+ // Stop recursing this tree if we are setting permission only and if
+ // SetRplPerms tells us to quit this branch. Here we depend on the fact that
+ // RPL_SD_BLOCK - structure not visible here, contains BOOL StopRecursion
+ // as its first element.
+ //
+ if ( Auxiliary) {
+ Error = (AuxiliaryCallback)( ((Copy)?Target:Source), pAuxiliaryBlock);
+ if (Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_AUXILIARY);
+ return( Error);
+ }
+ if ( !Delete && !Copy && *pAuxiliaryBlock == TRUE) {
+ return( NO_ERROR);
+ }
+ }
+
+ //
+ // Enumerate and copy/change/delete directory contents.
+ //
+ for ( Handle = INVALID_HANDLE_VALUE; NOTHING; Error = NO_ERROR) {
+
+ if ( Handle == INVALID_HANDLE_VALUE) {
+ Handle = FindFirstFile( CurrentSource, &FindData);
+ if (Handle == INVALID_HANDLE_VALUE) {
+ Error = GetLastError();
+ //
+ // ERROR_PATH_NOT_FOUND occurs for non-existing Source tree
+ //
+ if (Error == ERROR_NO_MORE_FILES || Error == ERROR_PATH_NOT_FOUND) {
+ Error = NO_ERROR;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d, CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, Flags);
+ }
+ break;
+ }
+ //
+ // Trim ALLFILES_SUFFIX off CurrentSource for future use
+ //
+ CurrentSource[ SourceLength + LEN_FILE_SEPARATOR] = L'\0';
+ } else {
+ if ( !FindNextFile( Handle, &FindData)) {
+ Error = GetLastError();
+ if ( Error == ERROR_NO_MORE_FILES) {
+ Error = NO_ERROR;
+ break;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d, CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, Flags);
+ }
+ }
+ }
+
+ if ( 0 == wcscmp( FindData.cFileName, HERE_DIRECTORY) ||
+ 0 == wcscmp( FindData.cFileName, PARENT_DIRECTORY)) {
+ continue; // get next entry
+ }
+ FileLength = wcslen( FindData.cFileName);
+
+ //
+ // Update source name.
+ //
+ if ( SourceLength + LEN_FILE_SEPARATOR + FileLength >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump(++RG_Assert,("Error=%d, SourceLength=%d, FileName=%ws",
+ Error, SourceLength, FindData.cFileName));
+ LoadError( Source, Error, Flags);
+ break;
+ }
+ memcpy( CurrentSource+SourceLength+LEN_FILE_SEPARATOR,
+ FindData.cFileName, (FileLength+1) * sizeof(WCHAR));
+
+ //
+ // Update target name (this is needed both for the file case &
+ // the directory case below).
+ //
+ if ( Copy) {
+ if ( TargetLength + LEN_FILE_SEPARATOR + FileLength >= MAX_PATH) {
+ Error = ERROR_FILENAME_EXCED_RANGE;
+ RplDump(++RG_Assert,("Error=%d, TargetLength=%d, FileName=%ws",
+ Error, TargetLength, FindData.cFileName));
+ LoadError( Target, Error, RPL_TREE_COPY);
+ break;
+ }
+ memcpy( CurrentTarget+TargetLength,
+ FILE_SEPARATOR, sizeof(FILE_SEPARATOR));
+ memcpy( CurrentTarget+TargetLength+LEN_FILE_SEPARATOR,
+ FindData.cFileName, (FileLength+1) * sizeof(WCHAR));
+ }
+
+ if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ if ( Copy) {
+ if ( !CopyFile( CurrentSource, CurrentTarget, FALSE)) {
+ Error = GetLastError();
+ RplDump(++RG_Assert,("Error=%d CurrentSource=%ws CurrentTarget=%ws",
+ Error, CurrentSource, CurrentTarget));
+ LoadError( CurrentSource, Error, RPL_TREE_COPY);
+ break;
+ }
+ } else if ( Delete){
+ //
+ // If file has readonly attribute, we need to reset this
+ // attribute, otherwise DeleteFile() with fail with error
+ // access denied.
+ //
+ if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)) {
+ (VOID)SetFileAttributes( CurrentSource, FILE_ATTRIBUTE_NORMAL);
+ }
+ if ( !DeleteFile( CurrentSource)) {
+ Error = GetLastError();
+ RplDump(++RG_Assert,("Error=%d CurrentSource=%ws", Error, CurrentSource));
+ LoadError( CurrentSource, Error, RPL_TREE_DELETE);
+ break;
+ }
+ }
+
+ //
+ // Set permissions after file is created (in Copy && Auxiliary case)
+ //
+ if ( Auxiliary) {
+ Error = (AuxiliaryCallback)( ( (Copy) ? CurrentTarget
+ : CurrentSource ),
+ pAuxiliaryBlock );
+ if (Error != NERR_Success) {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_AUXILIARY);
+ break;
+ }
+ }
+
+ } else {
+ Error = RplDoTree( CurrentSource,
+ CurrentTarget,
+ Flags,
+ AuxiliaryCallback,
+ pAuxiliaryBlock);
+ }
+ }
+
+ if (Handle != INVALID_HANDLE_VALUE) {
+ FindClose( Handle );
+ }
+
+ //
+ // Delete is the only directory operation that is done at the end.
+ //
+ if ( Delete && Error == NO_ERROR) {
+ //
+ // If directory has readonly attribute, we need to reset this
+ // attribute, otherwise RemoveDirectory() with fail with error
+ // access denied.
+ //
+ (VOID)SetFileAttributes( Source, FILE_ATTRIBUTE_DIRECTORY);
+ if ( !RemoveDirectory( Source)) {
+ Error = GetLastError();
+ if (Error == ERROR_FILE_NOT_FOUND || Error == ERROR_PATH_NOT_FOUND) {
+ Error = NO_ERROR;
+ } else {
+ RplDump( ++RG_Assert, ( "Error=%d", Error));
+ LoadError( Source, Error, RPL_TREE_DELETE);
+ }
+ }
+ }
+ return( Error);
+}
+
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target)
+{
+ DWORD Error;
+
+ Error = RplDoTree( Source, Target, RPL_TREE_COPY,
+ NULL, NULL);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD RplTreeDelete( IN PWCHAR Source)
+{
+ DWORD Error;
+
+ Error = RplDoTree( Source, L"", RPL_TREE_DELETE,
+ NULL, NULL);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD RplMakeDir( IN PWCHAR Source)
+{
+ DWORD Error = NO_ERROR;
+ if ( !CreateDirectory( Source, NULL)) {
+ Error = GetLastError();
+ RplDump( ++RG_Assert, ("Error=%d", Error));
+ }
+ return( Error);
+}
+
diff --git a/private/net/svcdlls/rpl/server/tree.h b/private/net/svcdlls/rpl/server/tree.h
new file mode 100644
index 000000000..d68d34917
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/tree.h
@@ -0,0 +1,28 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Exports from tree.c
+
+Author:
+
+ Jon Newman (jonn) 23 - November - 1993
+
+Revision History:
+
+ Vladimir Z. Vulovic (vladimv) 02 - December - 1993
+ Integrated with the rest of RPL service code.
+
+--*/
+
+DWORD RplTreeCopy( IN PWCHAR Source, IN PWCHAR Target);
+DWORD RplTreeDelete( IN PWCHAR Target);
+DWORD RplMakeDir( IN PWCHAR Target);
+
+
diff --git a/private/net/svcdlls/rpl/server/treei.h b/private/net/svcdlls/rpl/server/treei.h
new file mode 100644
index 000000000..019dee4d1
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/treei.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tree.h
+
+Abstract:
+
+ Internal definitions from tree.c
+
+Author:
+
+ Jon Newman (jonn) 16 - February - 1994
+
+Revision History:
+
+ Jon Newman (jonn) 16 - February - 1994
+ Added RplGrant*Perms primitives
+
+--*/
+
+typedef DWORD (RPL_TREE_CALLBACK)( PWCHAR Path, PBOOL pAuxiliaryBlock);
+
+#define RPL_TREE_AUXILIARY 0x2000 // perform callback action
+#define RPL_TREE_COPY 0x4000
+#define RPL_TREE_DELETE 0x8000
+
+//
+// Forward declarations
+//
+
+DWORD RplDoTree(
+ PWCHAR Source,
+ PWCHAR Target,
+ DWORD Flags,
+ RPL_TREE_CALLBACK AuxiliaryCallback,
+ PBOOL pAuxiliaryBlock
+ );
+
+VOID LoadError(
+ PWCHAR FilePath,
+ DWORD ActionError,
+ DWORD ActionFlags
+ );
+
diff --git a/private/net/svcdlls/rpl/server/vendor.c b/private/net/svcdlls/rpl/server/vendor.c
new file mode 100644
index 000000000..708a1b26c
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/vendor.c
@@ -0,0 +1,528 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ config.c
+
+Abstract:
+
+ This module contains RPL config apis: ConfigEnum.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 05 - November - 1993
+
+Revision History:
+
+ 05-Nov-1993 vladimv
+ Created
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "db.h"
+#include "dblib.h"
+#define RPLVENDOR_ALLOCATE
+#include "vendor.h"
+#undef RPLVENDOR_ALLOCATE
+
+
+DWORD VendorSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+ switch( Level) {
+ case 1:
+ //
+ // Must initialize Flags - or will trap in GetField.
+ //
+ {
+ *pErrorParameter = VENDOR_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->VendorComment != NULL) {
+ *pErrorParameter = VENDOR_VendorComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorComment].ColumnId,
+ Info->VendorComment,
+ ( wcslen( Info->VendorComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->VendorName != NULL) {
+ *pErrorParameter = VENDOR_VendorName;
+ CallM( JetSetColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ VENDOR_VendorName].ColumnId,
+ Info->VendorName,
+ ( wcslen( Info->VendorName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+DWORD VendorGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case VENDOR_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->VendorTableId,
+ VendorTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplVendorInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplVendorInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD VendorGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR VendorName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT PINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ Error = VendorGetField( pSession, VENDOR_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = VendorGetField( pSession, VENDOR_VendorComment, &Info->VendorComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( VendorName == NULL) {
+ Error = VendorGetField( pSession, VENDOR_VendorName, &Info->VendorName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( VendorName) + 1) * sizeof(WCHAR);
+ Info->VendorName = MIDL_user_allocate( DataSize);
+ if ( Info->VendorName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, ( "VendorName=0x%x", Info->VendorName));
+ memcpy( Info->VendorName, VendorName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID VendorGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info = Buffer;
+
+ switch( Level) {
+ case 1:
+ NOTHING; // fall through
+ case 0:
+ if ( Info->VendorComment != NULL) {
+ MIDL_user_free( Info->VendorComment);
+ }
+ if ( Info->VendorName != NULL) {
+ MIDL_user_free( Info->VendorName);
+ }
+ break;
+ }
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ OUT LPRPL_VENDOR_INFO_STRUCT VendorInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_VENDOR_INFO_1 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ Buffer = Info = VendorInfoStruct->VendorInfo1;
+
+ switch( Level) {
+ case 1:
+ if ( Info->Flags != 0) {
+ ErrorParameter = VENDOR_Flags;
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->VendorComment)) {
+ ErrorParameter = VENDOR_VendorComment;
+ break;
+ }
+ if ( !ValidHexName( Info->VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ ErrorParameter = VENDOR_VendorName;
+ break;
+ }
+ _wcsupr( Info->VendorName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that VendorName is available in the database.
+ //
+ if ( RplFind( pSession, VENDOR_TABLE_TAG, Info->VendorName)) {
+ Error = NERR_RplVendorNameUnavailable;
+ goto cleanup;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->VendorTableId, JET_prepInsert));
+
+ Error = VendorSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->VendorTableId, NULL, 0, NULL));
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR VendorName
+ )
+/*++
+ If VendorName is provided then delete matching record, else delete all
+ adapter records.
+--*/
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidHexName( VendorName, RPL_VENDOR_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( VendorName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, VENDOR_TABLE_TAG, VendorName)) {
+ Error = NERR_RplVendorNotFound;
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->VendorTableId));
+ Error = NO_ERROR;
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplVendorEnum(
+ IN RPL_RPC_HANDLE ServerHandle,
+ IN OUT LPRPL_VENDOR_ENUM VendorEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ pServerHandle - ptr to RPL_HANDLE
+
+ InfoStruct - Pointer to a structure that contains the information that
+ RPC needs about the returned data. This structure contains the
+ following information:
+ Level - The desired information level - indicates how to
+ interpret the structure of the returned buffer.
+ EntriesRead - Indicates how many elements are returned in the
+ array of structures that are returned.
+ BufferPointer - Location for the pointer to the array of
+ structures that are being returned.
+
+ PrefMaxLen - Indicates a maximum size limit that the caller will allow
+ for (the return buffer.
+
+ TotalEntries - Pointer to a value that upon return indicates the total
+ number of entries in the "active" database.
+
+ pResumeHandle - Inidcates where to restart the enumeration. This is an
+ optional parameter and can be NULL.
+
+Return Value:
+ NO_ERROR if success.
+
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ switch( VendorEnum->Level) {
+ case 1:
+ TypicalSize = CoreSize = sizeof( RPL_VENDOR_INFO_1);
+ NOTHING; // fall through
+ case 0:
+ if ( VendorEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_VENDOR_INFO_0);
+ }
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of VendorComment
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of VendorName
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ //
+ // If the caller has not specified a size, calculate a size
+ // that will hold the entire enumeration.
+ //
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ //
+ // Buffer space is shared by the array and by strings pointed at
+ // by the elements in this array. We need to decide up front how much
+ // space is allocated for array and how much for the strings.
+ //
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ //
+ // Note that MIDL_user_allocate() returns memory which is NOT initialized
+ // to zero. Since we do NOT use allocate all nodes, this means that all
+ // fields, especially pointers, in array elements must be properly set.
+ //
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, (
+ "VendorEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ VendorEnum->VendorInfo.Level0->Buffer = (LPRPL_VENDOR_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFilterFirst( pSession, VENDOR_TABLE_TAG, NULL, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = VendorGetInfo( pSession, NULL, VendorEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ if ( !RplFilterNext( pSession, pSession->VendorTableId, NULL, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ //
+ // We have space available but allocated array is not big enough.
+ // This should NOT happen often as our intent (see above) is to
+ // overestimate array length. When it happens we can still try
+ // to reallocate array to a larger size here. This is not done
+ // for now (too cumbersome) & we just stop the enumeration.
+ //
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ VendorGetInfoCleanup( VendorEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ VendorGetInfoCleanup( VendorEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_VENDOR, ("VendorEnum: EntriesRead = 0x%x", EntriesRead));
+
+ VendorEnum->VendorInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ VendorEnum->VendorInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ RplFilterSave( pSession, (DWORD)ServerHandle, NULL,
+ ((LPRPL_VENDOR_INFO_0)(Buffer-CoreSize))->VendorName,
+ pResumeHandle);
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+ return( Error);
+}
diff --git a/private/net/svcdlls/rpl/server/wcst.c b/private/net/svcdlls/rpl/server/wcst.c
new file mode 100644
index 000000000..2b9aa40be
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/wcst.c
@@ -0,0 +1,54 @@
+/*++
+
+Module Name:
+
+ wcst.c - to verify a fix for "D0H" strings.
+
+Abstract:
+
+
+--*/
+
+
+
+#include "local.h"
+
+DWORD StringToDword( IN PWCHAR String)
+/*++
+ We would like to use generic base (0) but it does not work for
+ strings like "D0H". That is the reason why we first check if
+ the last character is 'H' or 'h'.
+--*/
+{
+ DWORD Length;
+
+ Length = wcslen( String);
+ if ( Length == 0) {
+ return( 0);
+ }
+ if ( String[ Length-1] == L'H' || String[ Length-1] == L'h') {
+ return( wcstoul( String, NULL, 16));
+ } else {
+ return( wcstoul( String, NULL, 0));
+ }
+}
+
+void report ( IN PWCHAR String, IN DWORD Base)
+{
+ PWCHAR End;
+ DWORD Number;
+ Number = wcstoul( String, &End, Base);
+ printf( "String = %ws, End = %ws, Base = %d, Number = 0x%x\n", String, End, Base, Number);
+ printf( "StringToDword(%ws)= 0x%x\n", String, StringToDword( String));
+}
+
+VOID _CRTAPI1 main ( VOID)
+{
+ report( L"D0H", 0); // End = D0H, Number = 0x0
+ report( L"D0H", 16); // End = H, Number = 0xd0
+ report( L"D0", 0); // End = D0, Number = 0x0
+ report( L"D0", 16); // End = , Number = 0xd0
+ report( L"0xD0", 0); // End = , Number = 0xd0
+ report( L"0xD0", 16); // End = , Number = 0xd0
+}
+
diff --git a/private/net/svcdlls/rpl/server/wksta.c b/private/net/svcdlls/rpl/server/wksta.c
new file mode 100644
index 000000000..ef65e9174
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/wksta.c
@@ -0,0 +1,1471 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ wksta.c
+
+Abstract:
+
+ Wksta APIs.
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Revision History:
+
+--*/
+
+#include "local.h"
+#include "rpldb.h"
+#include "database.h" // RplDbFindWksta()
+#include "db.h"
+#include "dblib.h"
+#include "profile.h"
+#define RPLWKSTA_ALLOCATE
+#include "wksta.h"
+#undef RPLWKSTA_ALLOCATE
+
+
+DWORD WkstaSessionDel( IN PWCHAR WkstaName)
+{
+ DWORD Error;
+ WCHAR UncWkstaName[ MAX_PATH + sizeof(DOUBLE_BACK_SLASH_STRING)];
+
+ memcpy( UncWkstaName, DOUBLE_BACK_SLASH_STRING, sizeof(DOUBLE_BACK_SLASH_STRING));
+ wcscat( UncWkstaName, WkstaName);
+
+ Error = NetSessionDel( NULL, UncWkstaName, NULL);
+ if ( Error == NO_ERROR || Error == NERR_ClientNameNotFound) {
+ return( NO_ERROR);
+ }
+ RplDump( ++RG_Assert, ("Error=%d", Error));
+ return( Error);
+}
+
+
+DWORD WkstaGetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ OUT LPVOID * pData,
+ IN OUT LPINT pSpaceLeft
+ )
+/*++
+ Currently this routine always allocates buffer for variable length data.
+ It would be nice to relax this assumption - so that buffer gets allocated
+ if and only if *pData is NULL. We would then have to redefine meaning of
+ pSpaceLeft: rename it into pDataSize which on entry points to the size of
+ data buffer & on return to the size of data returned. The caller would
+ then have the responsibility of subtracting the amount of space left.
+
+ Also, various GetField routines should be unified.
+
+ BUGBUG This is too much of a change for this late moment.
+--*/
+{
+ BYTE LocalBuffer[ 300];
+ PBYTE Buffer;
+ DWORD DataSize;
+ DWORD BufferSize;
+ JET_ERR JetError;
+
+ switch( FieldIndex) {
+ case WKSTA_TcpIpAddress:
+ case WKSTA_TcpIpSubnet:
+ case WKSTA_TcpIpGateway:
+ case WKSTA_Flags:
+ Buffer = (PBYTE)pData;
+ BufferSize = sizeof( DWORD);
+ break;
+ default:
+#ifdef NOT_YET
+ if ( *pData == NULL) {
+#endif
+ Buffer = LocalBuffer;
+ BufferSize = sizeof( LocalBuffer);
+#ifdef NOT_YET
+ } else {
+ Buffer = *pData;
+ BufferSize = *pSpaceLeft;
+ }
+#endif
+ break;
+ }
+ JetError = JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ FieldIndex].ColumnId, Buffer,
+ BufferSize, &DataSize, 0, NULL);
+ if ( JetError < 0) {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ if ( Buffer != LocalBuffer) {
+ if ( BufferSize == DataSize) {
+ return( NO_ERROR);
+ } else if ( DataSize == 0) {
+ //
+ // This happens if we never defined this value. Set (-1)
+ // as an invalid tcp/ip address.
+ //
+ *(PDWORD)pData = (DWORD)-1;
+ return( NO_ERROR);
+ } else {
+ RplDump( ++RG_Assert, ("Bad DataSize=0x%x", DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ }
+ //
+ // We have done with fixed data. From here on we deal with unicode
+ // strings only.
+ //
+ if ( DataSize > sizeof( LocalBuffer)) {
+ RplDump( ++RG_Assert, ( "Too big DataSize=0x%x", DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ if ( DataSize == 0) {
+ if ( JetError != JET_wrnColumnNull) {
+ RplDump( ++RG_Assert, ( "JetError=%d", JetError));
+ return( NERR_RplWkstaInfoCorrupted);
+ } else {
+ *pData = NULL; // so RPC rpcrt4!_tree_size_ndr() does not bomb here
+ return( NO_ERROR);
+ }
+ }
+ if ( DataSize & 1 != 0 || wcslen((PWCHAR)LocalBuffer) + 1 != DataSize/2) {
+ RplDump( ++RG_Assert, ("LocalBuffer=0x%x, DataSize=0x%x", LocalBuffer, DataSize));
+ return( NERR_RplWkstaInfoCorrupted);
+ }
+ *pData = MIDL_user_allocate( DataSize);
+ if ( *pData == NULL) {
+ RplDump( ++RG_Assert, ( "Error=%d", GetLastError()));
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memcpy( *pData, LocalBuffer, DataSize);
+ *pSpaceLeft -= DataSize;
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaGetInfo(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ OUT LPVOID Buffer,
+ IN OUT LPINT pSpaceLeft
+ )
+{
+ DWORD Error;
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+
+ switch( Level) {
+ case 2:
+ Error = WkstaGetField( pSession, WKSTA_TcpIpGateway, (LPVOID *)&Info->TcpIpGateway, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_TcpIpSubnet, (LPVOID *)&Info->TcpIpSubnet, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_TcpIpAddress, (LPVOID *)&Info->TcpIpAddress, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_AdapterName, &Info->AdapterName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_FitFile, &Info->FitFile, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_BootName, &Info->BootName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 1:
+ Error = WkstaGetField( pSession, WKSTA_ProfileName, &Info->ProfileName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Info->Flags, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ NOTHING; // fall through
+ case 0:
+ Error = WkstaGetField( pSession, WKSTA_WkstaComment, &Info->WkstaComment, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ if ( WkstaName == NULL) {
+ Error = WkstaGetField( pSession, WKSTA_WkstaName, &Info->WkstaName, pSpaceLeft);
+ if ( Error != NO_ERROR) {
+ return( Error);
+ }
+ } else {
+ DWORD DataSize = (wcslen( WkstaName) + 1) * sizeof(WCHAR);
+ Info->WkstaName = MIDL_user_allocate( DataSize);
+ if ( Info->WkstaName == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ( "WkstaName=0x%x", Info->WkstaName));
+ memcpy( Info->WkstaName, WkstaName, DataSize);
+ *pSpaceLeft -= DataSize;
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+
+VOID WkstaGetInfoCleanup(
+ IN DWORD Level,
+ IN OUT LPVOID Buffer
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+ if ( Info->AdapterName != NULL) {
+ MIDL_user_free( Info->AdapterName);
+ }
+ if ( Info->FitFile != NULL) {
+ MIDL_user_free( Info->FitFile);
+ }
+ if ( Info->BootName != NULL) {
+ MIDL_user_free( Info->BootName);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->ProfileName != NULL) {
+ MIDL_user_free( Info->ProfileName);
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->WkstaComment != NULL) {
+ MIDL_user_free( Info->WkstaComment);
+ }
+ if ( Info->WkstaName != NULL) {
+ MIDL_user_free( Info->WkstaName);
+ }
+ break;
+ }
+}
+
+
+
+BOOL WkstaScan(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN DWORD ProfileNameSize,
+ IN OUT PBOOL pTableEnd
+ )
+{
+ JET_ERR JetError;
+
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT);
+ if ( JetError != JET_errSuccess) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaScan: JetSeek => %d", JetError));
+ *pTableEnd = TRUE;
+ return( TRUE);
+ }
+ //
+ // Verify that current wksta has the desired profile & at the
+ // same time set index range to be used with JetMove( Next).
+ //
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName,
+ ProfileNameSize, JET_bitNewKey));
+ JetError = JetSetIndexRange( pSession->SesId, pSession->WkstaTableId,
+ JET_bitRangeInclusive | JET_bitRangeUpperLimit);
+ if ( JetError != JET_errSuccess) {
+ *pTableEnd = TRUE;
+ }
+ return( TRUE);
+}
+
+
+
+BOOL WkstaResumeFirst(
+ IN PRPL_SESSION pSession,
+ IN LPWSTR ProfileName,
+ IN LPDWORD pResumeHandle,
+ OUT PBOOL pTableEnd
+ )
+/*++
+ Set currency to the first wksta for this profile, following
+ the wksta described by the resume handle.
+--*/
+{
+ BYTE ResumeValue[ RPL_MAX_PROFILE_NAME_SIZE + RPL_MAX_WKSTA_NAME_SIZE];
+ DWORD ResumeSize;
+ JET_ERR JetError;
+ DWORD ProfileNameSize;
+ DWORD WkstaNameSize;
+ WCHAR WkstaName[ RPL_MAX_WKSTA_NAME_LENGTH + 1 + JETBUG_STRING_LENGTH];
+
+ *pTableEnd = FALSE;
+
+ if ( ProfileName == NULL) {
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_WkstaName));
+ } else {
+ ProfileNameSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
+ }
+ //
+ // The call to move to the beginning of the table is not redundant.
+ // E.g. JET_errNoCurrentRecord will be returned in case of empty table.
+ //
+ JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveFirst, 0);
+ if ( JetError < 0) {
+ if ( JetError == JET_errRecordNotFound
+ || JetError == JET_errNoCurrentRecord) {
+ *pTableEnd = TRUE;
+ return( TRUE);
+ } else {
+ RplDump( ++RG_Assert, ("JetError=%d", JetError));
+ return( FALSE);
+ }
+ }
+ if ( (ARGUMENT_PRESENT( pResumeHandle)) && *pResumeHandle != 0) {
+ ResumeSize = sizeof( ResumeValue);
+ if ( !ResumeKeyGet( pSession, *pResumeHandle, ResumeValue, &ResumeSize)) {
+ return( FALSE);
+ }
+ if ( ProfileName == NULL) {
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ResumeValue, ResumeSize, JET_bitNewKey));
+ CallB( JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGT));
+ return( TRUE);
+ }
+ if ( ProfileNameSize != (wcslen( (PWCHAR)ResumeValue) + 1) * sizeof( WCHAR)
+ || memcmp( ProfileName, ResumeValue, ProfileNameSize)) {
+ RplDump( ++RG_Assert, ("ResumeValue=0x%x"));
+ return( FALSE);
+ }
+ WkstaNameSize = (wcslen( (PWCHAR)(ResumeValue + ProfileNameSize)) + 1) * sizeof(WCHAR);
+ memcpy( WkstaName, ResumeValue + ProfileNameSize, WkstaNameSize);
+ } else {
+ if ( ProfileName == NULL) {
+ return( TRUE);
+ }
+ WkstaNameSize = 0;
+ }
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
+ if ( WkstaNameSize != 0) {
+#ifdef RPL_JETBUG
+ memcpy( (PBYTE)WkstaName + WkstaNameSize, JETBUG_STRING, JETBUG_STRING_SIZE);
+ WkstaNameSize += JETBUG_STRING_LENGTH * sizeof(WCHAR);
+#endif
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, WkstaName, WkstaNameSize, 0));
+ }
+ return( WkstaScan( pSession, ProfileName, ProfileNameSize, pTableEnd));
+}
+
+
+VOID WkstaResumeSave(
+ IN PRPL_SESSION pSession,
+ IN DWORD ServerHandle,
+ IN PWCHAR ProfileName,
+ IN PWCHAR WkstaName,
+ IN PDWORD pResumeHandle
+ )
+{
+ BYTE ResumeBuffer[ 30 * sizeof(WCHAR)];
+ DWORD ResumeSize;
+ DWORD WkstaSize;
+ if ( ProfileName != NULL) {
+ ResumeSize = (wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer, ProfileName, ResumeSize);
+ } else {
+ ResumeSize = 0;
+ }
+ WkstaSize = ( wcslen( WkstaName) + 1) * sizeof(WCHAR);
+ memcpy( ResumeBuffer + ResumeSize, WkstaName, WkstaSize);
+ ResumeSize += WkstaSize;
+ (VOID)ResumeKeySet( pSession, (DWORD)ServerHandle, ResumeBuffer, ResumeSize, pResumeHandle);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaEnum(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR ProfileName,
+ IN OUT LPRPL_WKSTA_ENUM WkstaEnum,
+ IN DWORD PrefMaxLength,
+ OUT LPDWORD TotalEntries,
+ OUT LPDWORD pResumeHandle OPTIONAL
+ )
+/*++
+ For more extensive comments see related code in NetrConfigEnum.
+--*/
+{
+ LPBYTE Buffer;
+ DWORD TypicalSize;
+ DWORD CoreSize;
+ DWORD Error;
+ INT SpaceLeft;
+ DWORD ArrayLength;
+ DWORD EntriesRead;
+ JET_ERR JetError;
+ BOOL InfoError;
+ BOOL TableEnd;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( ProfileName != NULL) {
+ _wcsupr( ProfileName);
+ }
+
+ switch( WkstaEnum->Level) {
+ case 2:
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_2);
+ TypicalSize += 14 * sizeof( WCHAR); // typical size of AdapterName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of FitFile
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of BootName
+ NOTHING; // fall through
+ case 1:
+ if ( WkstaEnum->Level == 1) {
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_1);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of ProfileName
+ NOTHING; // fall through
+ case 0:
+ if ( WkstaEnum->Level == 0) {
+ TypicalSize = CoreSize = sizeof( RPL_WKSTA_INFO_0);
+ }
+ TypicalSize += 8 * sizeof( WCHAR); // typical size of WkstaName
+ TypicalSize += 20 * sizeof( WCHAR); // typical size of WkstaComment
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( PrefMaxLength == -1) {
+ SpaceLeft = DEFAULT_BUFFER_SIZE;
+ } else {
+ SpaceLeft = PrefMaxLength;
+ }
+
+ ArrayLength = SpaceLeft / TypicalSize;
+ if ( ArrayLength == 0) {
+ ArrayLength = 1; // try to return at least one element
+ }
+
+ Buffer = MIDL_user_allocate( ArrayLength * CoreSize);
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, (
+ "WkstaEnum: Buffer=0x%x, ArrayLength=0x%x", Buffer, ArrayLength));
+
+ WkstaEnum->WkstaInfo.Level0->Buffer = (LPRPL_WKSTA_INFO_0)Buffer;
+
+ EntriesRead = 0;
+ InfoError = FALSE;
+ Error = NO_ERROR;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !WkstaResumeFirst( pSession, ProfileName, pResumeHandle, &TableEnd)) {
+ Error = NERR_RplCannotEnum;
+ goto cleanup;
+ }
+ if ( TableEnd == TRUE) {
+ goto cleanup;
+ }
+ for ( ; ; ) {
+ memset( Buffer, 0, CoreSize); // for cleanup to work properly
+ Error = WkstaGetInfo( pSession, NULL, WkstaEnum->Level, Buffer, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ InfoError = TRUE; // clean things up without holding crit sec
+ break;
+ }
+ EntriesRead++;
+ Buffer += CoreSize;
+ SpaceLeft -= CoreSize;
+ JetError = JetMove( pSession->SesId, pSession->WkstaTableId, JET_MoveNext, 0);
+ if ( JetError != JET_errSuccess) {
+ break; // assume end of table
+ }
+ if ( SpaceLeft <= 0) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ if ( EntriesRead >= ArrayLength) {
+ Error = ERROR_MORE_DATA;
+ break;
+ }
+ }
+cleanup:
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ if ( InfoError == TRUE) {
+ WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
+ }
+ if ( Error == NO_ERROR) {
+ *TotalEntries = EntriesRead;
+ } else if ( Error == ERROR_MORE_DATA) {
+ *TotalEntries = EntriesRead * 2; // we cheat here
+ } else {
+ //
+ // Cleanup in case of "bad" errors.
+ //
+ while ( EntriesRead > 0) {
+ EntriesRead--;
+ Buffer -= CoreSize;
+ WkstaGetInfoCleanup( WkstaEnum->Level, Buffer);
+ }
+ MIDL_user_free( Buffer);
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WKSTA, ("WkstaEnum: EntriesRead = 0x%x", EntriesRead));
+
+ WkstaEnum->WkstaInfo.Level0->EntriesRead = EntriesRead;
+ if ( EntriesRead == 0) {
+ WkstaEnum->WkstaInfo.Level0->Buffer = NULL;
+ }
+
+ if ( ARGUMENT_PRESENT( pResumeHandle)) {
+ if ( Error == ERROR_MORE_DATA && EntriesRead > 0) {
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+ WkstaResumeSave( pSession, (DWORD)ServerHandle, ProfileName,
+ ((LPRPL_WKSTA_INFO_0)(Buffer-CoreSize))->WkstaName,
+ pResumeHandle
+ );
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ } else {
+ *pResumeHandle = 0; // resume from beginning
+ }
+ }
+
+ return( Error);
+}
+
+
+BOOL WkstaFindFirst(
+ IN PRPL_SESSION pSession,
+ IN PWCHAR ProfileName
+ )
+/*++
+ Returns TRUE if it can find a record (the first record) using input
+ profile name. Returns FALSE otherwise.
+
+ We cannot use SetIndexRange technique here because ProfileName is only
+ the first part of an index & we would never find a match.
+--*/
+{
+ DWORD ProfileNameSize;
+ JET_ERR JetError;
+ BYTE Data[ 300];
+ DWORD DataSize;
+
+ CallB( JetSetCurrentIndex( pSession->SesId, pSession->WkstaTableId, WKSTA_INDEX_ProfileNameWkstaName));
+ ProfileNameSize = ( wcslen( ProfileName) + 1) * sizeof(WCHAR);
+ CallB( JetMakeKey( pSession->SesId, pSession->WkstaTableId, ProfileName, ProfileNameSize, JET_bitNewKey));
+ JetError = JetSeek( pSession->SesId, pSession->WkstaTableId, JET_bitSeekGE);
+ if ( JetError < 0) {
+ return( FALSE);
+ }
+ CallB( JetRetrieveColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId, Data,
+ sizeof( Data), &DataSize, 0, NULL));
+ if ( ProfileNameSize != DataSize) {
+ return( FALSE);
+ }
+ if ( memcmp( ProfileName, Data, DataSize) != 0) {
+ return( FALSE);
+ }
+ return( TRUE); // we found wksta record using this profile
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaGetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ OUT LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct
+ )
+{
+ DWORD Error;
+ LPBYTE Buffer;
+ DWORD DataSize;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( WkstaName);
+
+ switch( Level) {
+ case 0:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_0));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_0));
+ WkstaInfoStruct->WkstaInfo0 = (LPRPL_WKSTA_INFO_0)Buffer;
+ break;
+ case 1:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_1));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_1));
+ WkstaInfoStruct->WkstaInfo1 = (LPRPL_WKSTA_INFO_1)Buffer;
+ break;
+ case 2:
+ Buffer = MIDL_user_allocate( sizeof( RPL_WKSTA_INFO_2));
+ if ( Buffer == NULL) {
+ return( ERROR_NOT_ENOUGH_MEMORY);
+ }
+ memset( Buffer, 0, sizeof( RPL_WKSTA_INFO_2));
+ WkstaInfoStruct->WkstaInfo2 = (LPRPL_WKSTA_INFO_2)Buffer;
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ } else {
+ Error = WkstaGetInfo( pSession, WkstaName, Level, Buffer, &DataSize);
+ }
+
+ Call( JetCommitTransaction( pSession->SesId, 0));
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( Error != NO_ERROR) {
+ WkstaGetInfoCleanup( Level, Buffer);
+ switch( Level) {
+ case 0:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo0 = NULL;
+ break;
+ case 1:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo1 = NULL;
+ break;
+ case 2:
+ MIDL_user_free( Buffer);
+ WkstaInfoStruct->WkstaInfo2 = NULL;
+ break;
+ }
+ }
+ return( Error);
+}
+
+
+DWORD WkstaSetField(
+ IN PRPL_SESSION pSession,
+ IN DWORD FieldIndex,
+ IN LPVOID Data,
+ IN DWORD DataSize
+ )
+{
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ FieldIndex].ColumnId, Data, DataSize, 0, NULL));
+ return( NO_ERROR);
+}
+
+
+DWORD WkstaSetInfo(
+ IN PRPL_SESSION pSession,
+ IN DWORD Level,
+ IN LPVOID Buffer,
+ OUT LPDWORD pErrorParameter
+ )
+/*++
+ -1 used to be a special "do not change value" for TcpIp columns.
+ This was discontinued. The reason is, the only way for an admin
+ to disable TCP/IP on the client, is to set TcpIp columns to -1 which
+ then causes rpl service not to send TCP/IP addresss to rpl client.
+
+ BUGBUG Whole TCP/IP approach has to be revisited, taking DHCP into account.
+--*/
+{
+ LPRPL_WKSTA_INFO_2 Info = Buffer;
+ switch( Level) {
+ case 2:
+// if ( Info->TcpIpGateway != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpGateway;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpGateway].ColumnId,
+ &Info->TcpIpGateway, sizeof( Info->TcpIpGateway), 0, NULL));
+ }
+// if ( Info->TcpIpSubnet != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpSubnet;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpSubnet].ColumnId,
+ &Info->TcpIpSubnet, sizeof( Info->TcpIpSubnet), 0, NULL));
+ }
+// if ( Info->TcpIpAddress != -1) {
+ {
+ *pErrorParameter = WKSTA_TcpIpAddress;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_TcpIpAddress].ColumnId,
+ &Info->TcpIpAddress, sizeof( Info->TcpIpAddress), 0, NULL));
+ }
+ if ( Info->AdapterName != NULL) {
+ *pErrorParameter = WKSTA_AdapterName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_AdapterName].ColumnId,
+ Info->AdapterName,
+ ( wcslen( Info->AdapterName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->FitFile != NULL) {
+ *pErrorParameter = WKSTA_FitFile;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_FitFile].ColumnId,
+ Info->FitFile,
+ ( wcslen( Info->FitFile) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->BootName != NULL) {
+ *pErrorParameter = WKSTA_BootName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_BootName].ColumnId,
+ Info->BootName,
+ ( wcslen( Info->BootName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( Info->ProfileName != NULL) {
+ *pErrorParameter = WKSTA_ProfileName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_ProfileName].ColumnId,
+ Info->ProfileName,
+ ( wcslen( Info->ProfileName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->Flags != 0) {
+ *pErrorParameter = WKSTA_Flags;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_Flags].ColumnId,
+ &Info->Flags, sizeof( Info->Flags), 0, NULL));
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( Info->WkstaComment != NULL) {
+ *pErrorParameter = WKSTA_WkstaComment;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaComment].ColumnId,
+ Info->WkstaComment,
+ ( wcslen( Info->WkstaComment) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ if ( Info->WkstaName != NULL) {
+ *pErrorParameter = WKSTA_WkstaName;
+ CallM( JetSetColumn( pSession->SesId, pSession->WkstaTableId,
+ WkstaTable[ WKSTA_WkstaName].ColumnId,
+ Info->WkstaName,
+ ( wcslen( Info->WkstaName) + 1) * sizeof(WCHAR),
+ 0, NULL));
+ }
+ break;
+ }
+ return( NO_ERROR);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaSetInfo(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName,
+ IN DWORD Level,
+ IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+/*++
+ Note that changes at info level 1 (ProfileName) imply that we need to
+ make changes to info level 2 (BootName & FitFile) parameters.
+
+ Here we pay for the redundancy in wksta records.
+--*/
+{
+ DWORD Error;
+ DWORD ErrorParameter;
+ LPVOID Buffer;
+ LPRPL_WKSTA_INFO_2 Info;
+ DWORD Flags;
+ DWORD Sharing;
+ DWORD LogonInput;
+ DWORD Dhcp;
+ DWORD DeleteUserAccount;
+ PWCHAR ProfileName;
+ DWORD TargetSharing;
+ DWORD TargetLogonInput;
+ DWORD TargetDhcp;
+ DWORD TargetDeleteUserAccount;
+ PWCHAR TargetProfileName;
+ PWCHAR TargetWkstaName;
+ INT SpaceLeft;
+ BOOL TreeModified;
+ PWCHAR FitFile;
+ PWCHAR BootName;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+ FitFile = NULL;
+ BootName = NULL;
+ TreeModified = FALSE;
+ Sharing = 0;
+ TargetSharing = 0;
+ ProfileName = NULL;
+ TargetProfileName = NULL;
+ _wcsupr( WkstaName);
+ TargetWkstaName = NULL;
+
+ Info = Buffer = WkstaInfoStruct->WkstaInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidName( Info->FitFile, RPL_MAX_STRING_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_FitFile;
+ break;
+ }
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_AdapterName;
+ break;
+ }
+ if ( Info->AdapterName != NULL) {
+ _wcsupr( Info->AdapterName);
+ }
+ NOTHING; // fall through
+ case 1:
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_ProfileName;
+ break;
+ }
+ if ( Info->ProfileName != NULL) {
+ _wcsupr( Info->ProfileName);
+ }
+ TargetProfileName = Info->ProfileName;
+ if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetSharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
+ switch ( TargetSharing) {
+ case 0:
+ case WKSTA_FLAGS_SHARING_TRUE:
+ case WKSTA_FLAGS_SHARING_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetLogonInput = Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
+ switch ( TargetLogonInput) {
+ case 0:
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetDhcp = Info->Flags & WKSTA_FLAGS_MASK_DHCP;
+ switch( TargetDhcp) {
+ case 0:
+ case WKSTA_FLAGS_DHCP_TRUE:
+ case WKSTA_FLAGS_DHCP_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ TargetDeleteUserAccount = Info->Flags & WKSTA_FLAGS_MASK_DELETE;
+ switch( TargetDeleteUserAccount) {
+ case 0:
+ case WKSTA_FLAGS_DELETE_TRUE:
+ case WKSTA_FLAGS_DELETE_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ NOTHING; // fall through
+ case 0:
+ if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
+ ErrorParameter = WKSTA_WkstaComment;
+ break;
+ }
+ if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, FALSE)) {
+ ErrorParameter = WKSTA_WkstaName;
+ break;
+ }
+ if ( Info->WkstaName != NULL) {
+ _wcsupr( Info->WkstaName);
+ //
+ // Take new wksta name into consideration only if it is different
+ // than old wksta name (we ignore NOOP requests).
+ //
+ if ( wcscmp( Info->WkstaName, WkstaName) != 0) {
+ TargetWkstaName = Info->WkstaName;
+ }
+ }
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+ ErrorParameter = 0;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( TargetWkstaName != NULL) {
+ //
+ // Verify that TargetWkstaName is available in the database.
+ // Once we pass this check, disk routines below will nuke any
+ // old stuff in existing TargetWkstaName trees.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ }
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaGetField( pSession, WKSTA_Flags, (LPVOID *)&Flags, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ Dhcp = Flags & WKSTA_FLAGS_MASK_DHCP;
+ DeleteUserAccount = Flags & WKSTA_FLAGS_MASK_DELETE;
+ LogonInput = Flags & WKSTA_FLAGS_MASK_LOGON_INPUT;
+ Sharing = Flags & WKSTA_FLAGS_MASK_SHARING;
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL
+ || TargetWkstaName != NULL) {
+ Error = WkstaSessionDel( WkstaName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( TargetSharing == Sharing) {
+ TargetSharing = 0; // ignore NOOP requests
+ }
+ Error = WkstaGetField( pSession, WKSTA_ProfileName, &ProfileName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( TargetProfileName != NULL && wcscmp( TargetProfileName, ProfileName) == 0) {
+ TargetProfileName = NULL; // ignore NOOP requests
+ }
+ }
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL) {
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG,
+ TargetProfileName != NULL ? TargetProfileName : ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ if ( TargetProfileName != NULL) {
+ Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ if ( !BootFind( pSession, BootName,
+ AdapterNameToVendorId( Info->AdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+ }
+ Error = ProfileGetField( pSession, (TargetSharing != 0 ? TargetSharing : Sharing)
+ == WKSTA_FLAGS_SHARING_TRUE ? PROFILE_FitShared : PROFILE_FitPersonal,
+ &FitFile, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ if ( TargetSharing != 0 || TargetProfileName != NULL
+ || TargetWkstaName != NULL) {
+ //
+ // Create new tree, or add new branches to the current tree.
+ //
+ TreeModified = TRUE; // to undo tree changes in case of errors below
+ Error = WkstaDiskSet( ADD_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ }
+
+ //
+ // Tree is ready and we can call jet to modify database wksta record.
+ //
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepReplace));
+ if ( TargetSharing == 0) {
+ //
+ // TargetSharing may have been reset to 0 due to being equal
+ // to Sharing. If so, then the line below is a no-op.
+ //
+ Info->Flags |= Sharing;
+ }
+ if ( TargetLogonInput == 0) {
+ Info->Flags |= LogonInput;
+ }
+ if ( TargetDhcp == 0) {
+ Info->Flags |= Dhcp;
+ }
+ if ( TargetDeleteUserAccount == 0) {
+ Info->Flags |= DeleteUserAccount;
+ }
+ Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ if ( BootName != NULL) {
+ Error = WkstaSetField( pSession, WKSTA_BootName, BootName,
+ (wcslen(BootName)+1)*sizeof(WCHAR));
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ if ( FitFile != NULL) {
+ Error = WkstaSetField( pSession, WKSTA_FitFile, FitFile,
+ (wcslen(FitFile)+1)*sizeof(WCHAR));
+ if ( Error != ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+
+ //
+ // Remove old tree, or old branches in the current tree.
+ //
+ WkstaDiskSet( DEL_OLD_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+
+cleanup:
+ if ( Error != NO_ERROR && TreeModified == TRUE) {
+ //
+ // Remove new tree, or new branches in the current tree.
+ //
+ WkstaDiskSet( DEL_NEW_BRANCHES, WkstaName, ProfileName, Sharing,
+ TargetWkstaName, TargetProfileName, TargetSharing);
+ }
+
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FitFile != NULL) {
+ MIDL_user_free( FitFile);
+ }
+ if ( BootName != NULL) {
+ MIDL_user_free( BootName);
+ }
+ if ( ProfileName != NULL) {
+ MIDL_user_free( ProfileName);
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaAdd(
+ IN RPL_HANDLE ServerHandle,
+ IN DWORD Level,
+ IN LPRPL_WKSTA_INFO_STRUCT WkstaInfoStruct,
+ OUT LPDWORD pErrorParameter OPTIONAL
+ )
+{
+ LPRPL_WKSTA_INFO_2 Info;
+ LPVOID Buffer;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ DWORD Sharing;
+ BOOL FreeBootName = FALSE;
+ BOOL FreeFitFile = FALSE;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ ErrorParameter = INVALID_ERROR_PARAMETER;
+
+ Buffer = Info = WkstaInfoStruct->WkstaInfo2;
+ switch( Level) {
+ case 2:
+ if ( !ValidHexName( Info->AdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_AdapterName;
+ break;
+ }
+ _wcsupr( Info->AdapterName);
+ if ( !ValidName( Info->ProfileName, RPL_MAX_PROFILE_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_ProfileName;
+ break;
+ }
+ _wcsupr( Info->ProfileName);
+ if ( Info->Flags & ~WKSTA_FLAGS_MASK) {
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ Sharing = Info->Flags & WKSTA_FLAGS_MASK_SHARING;
+ switch ( Sharing) {
+ case WKSTA_FLAGS_SHARING_TRUE:
+ case WKSTA_FLAGS_SHARING_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_LOGON_INPUT) {
+ case WKSTA_FLAGS_LOGON_INPUT_REQUIRED:
+ case WKSTA_FLAGS_LOGON_INPUT_OPTIONAL:
+ case WKSTA_FLAGS_LOGON_INPUT_IMPOSSIBLE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DHCP) {
+ case WKSTA_FLAGS_DHCP_TRUE:
+ case WKSTA_FLAGS_DHCP_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ switch( Info->Flags & WKSTA_FLAGS_MASK_DELETE) {
+ case WKSTA_FLAGS_DELETE_TRUE:
+ case WKSTA_FLAGS_DELETE_FALSE:
+ break;
+ default:
+ ErrorParameter = WKSTA_Flags;
+ break;
+ }
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ break;
+ }
+ if ( RPL_STRING_TOO_LONG( Info->WkstaComment)) {
+ ErrorParameter = WKSTA_WkstaComment;
+ break;
+ }
+ if ( !ValidName( Info->WkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ ErrorParameter = WKSTA_WkstaName;
+ break;
+ }
+ _wcsupr( Info->WkstaName);
+ break;
+ default:
+ return( ERROR_INVALID_LEVEL);
+ break;
+ }
+
+ if ( ErrorParameter != INVALID_ERROR_PARAMETER) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that WkstaName is available in the database.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, Info->WkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that AdapterName is available in the database.
+ //
+ if ( RplDbFindWksta( pSession, Info->AdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info->ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ //
+ // Verify that there is a boot block record for (AdapterName, ProfileName)
+ // pair. This in turn "validates" AdapterName.
+ //
+ if ( Info->BootName == NULL) {
+ Error = ProfileGetField( pSession, PROFILE_BootName, &Info->BootName, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ }
+ if ( !BootFind( pSession, Info->BootName, AdapterNameToVendorId( Info->AdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+
+ if ( Info->FitFile == NULL) {
+ Error = ProfileGetField( pSession, Sharing == WKSTA_FLAGS_SHARING_TRUE
+ ? PROFILE_FitShared : PROFILE_FitPersonal, &Info->FitFile,
+ &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeFitFile = TRUE;
+ }
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
+
+ Error = WkstaSetInfo( pSession, Level, Buffer, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ ErrorParameter = 0;
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+ }
+
+ Error = WkstaDiskAdd( TRUE, Info->WkstaName, Info->ProfileName, Sharing);
+ if ( Error != NO_ERROR) {
+ //
+ // WkstaDiskAdd deletion code does not care about ProfileName or Sharing
+ // We do not need to delete the new record since we are going to rollback
+ // the transaction.
+ //
+ WkstaDiskAdd( FALSE, Info->WkstaName, NULL, FALSE);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( Info->BootName);
+ Info->BootName = NULL;
+ }
+ if ( FreeFitFile == TRUE) {
+ MIDL_user_free( Info->FitFile);
+ Info->FitFile = NULL;
+ }
+ if ( Error != ERROR_SUCCESS) {
+ if ( ARGUMENT_PRESENT( pErrorParameter)) {
+ *pErrorParameter = ErrorParameter;
+ }
+ }
+ return( Error);
+}
+
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaDel(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR WkstaName
+ )
+{
+ DWORD Error;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ _wcsupr( WkstaName);
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, WkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaSessionDel( WkstaName);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ CallJ( JetDelete( pSession->SesId, pSession->WkstaTableId));
+ //
+ // WkstaDiskAdd deletion code does not care about ProfileName or Sharing
+ //
+ WkstaDiskAdd( FALSE, WkstaName, NULL, FALSE);
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+ return( Error);
+}
+
+
+NET_API_STATUS NET_API_FUNCTION
+NetrRplWkstaClone(
+ IN RPL_HANDLE ServerHandle,
+ IN LPWSTR SourceWkstaName,
+ IN LPWSTR TargetWkstaName,
+ IN LPWSTR TargetWkstaComment,
+ IN LPWSTR TargetAdapterName,
+ IN DWORD TargetTcpIpAddress
+ )
+{
+ RPL_WKSTA_INFO_2 Info;
+ DWORD Error;
+ DWORD ErrorParameter;
+ DWORD DataSize;
+ PWCHAR SaveWkstaName;
+ PWCHAR SaveWkstaComment;
+ PWCHAR SaveAdapterName;
+ PWCHAR BootName;
+ BOOL FreeBootName = FALSE;
+ INT SpaceLeft;
+ PRPL_SESSION pSession = &RG_ApiSession;
+
+ if ( !ValidName( SourceWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( SourceWkstaName);
+ if ( !ValidName( TargetWkstaName, RPL_MAX_WKSTA_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetWkstaName);
+ if ( !ValidHexName( TargetAdapterName, RPL_ADAPTER_NAME_LENGTH, TRUE)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+ _wcsupr( TargetAdapterName);
+ if ( RPL_STRING_TOO_LONG( TargetWkstaComment)) {
+ return( ERROR_INVALID_PARAMETER);
+ }
+
+ //
+ // Zero all the pointers so we can safely call WkstaGetInfoClenaup().
+ // in all the cases.
+ //
+ memset( &Info, 0, sizeof( Info));
+ SaveWkstaName = NULL;
+ SaveWkstaComment = NULL;
+ SaveAdapterName = NULL;
+
+ EnterCriticalSection( &RG_ProtectDatabase);
+ Call( JetBeginTransaction( pSession->SesId));
+
+ //
+ // Verify that TargetWkstaName is available in the database.
+ //
+ if ( RplFind( pSession, WKSTA_TABLE_TAG, TargetWkstaName)) {
+ Error = NERR_RplWkstaNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that TargetAdapterName is available in the database.
+ //
+ if ( RplDbFindWksta( pSession, TargetAdapterName)) {
+ Error = NERR_RplAdapterNameUnavailable;
+ goto cleanup;
+ }
+ //
+ // Verify that SourceWkstaName exists.
+ //
+ if ( !RplFind( pSession, WKSTA_TABLE_TAG, SourceWkstaName)) {
+ Error = NERR_RplWkstaNotFound;
+ goto cleanup;
+ }
+ Error = WkstaGetInfo( pSession, SourceWkstaName, 2, &Info, &DataSize);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ //
+ // Verify that Source profile is compatible with Target workstation.
+ //
+ if ( !RplFind( pSession, PROFILE_TABLE_TAG, Info.ProfileName)) {
+ Error = NERR_RplProfileNotFound;
+ goto cleanup;
+ }
+ Error = ProfileGetField( pSession, PROFILE_BootName, &BootName, &SpaceLeft);
+ if ( Error != NO_ERROR) {
+ goto cleanup;
+ }
+ FreeBootName = TRUE;
+ if ( !BootFind( pSession, BootName,
+ AdapterNameToVendorId( TargetAdapterName))) {
+ Error = NERR_RplIncompatibleProfile;
+ goto cleanup;
+ }
+ //
+ // Save source data, then overload target data.
+ //
+ SaveWkstaName = Info.WkstaName;
+ SaveWkstaComment = Info.WkstaComment;
+ SaveAdapterName = Info.AdapterName;
+ Info.WkstaName = TargetWkstaName;
+ Info.WkstaComment = TargetWkstaComment;
+ Info.AdapterName = TargetAdapterName;
+ Info.TcpIpAddress = TargetTcpIpAddress;
+
+ CallJ( JetPrepareUpdate( pSession->SesId, pSession->WkstaTableId, JET_prepInsert));
+
+ Error = WkstaSetInfo( pSession, 2, &Info, &ErrorParameter);
+ if ( Error == ERROR_SUCCESS) {
+ CallJ( JetUpdate( pSession->SesId, pSession->WkstaTableId, NULL, 0, NULL));
+ }
+
+ Error = WkstaDiskClone( TRUE, SourceWkstaName, TargetWkstaName);
+ if ( Error != NO_ERROR) {
+ //
+ // Delete new tree. We do not need to delete the new record
+ // since we are going to rollback the transaction.
+ //
+ WkstaDiskClone( FALSE, SourceWkstaName, TargetWkstaName);
+ }
+
+cleanup:
+ if ( Error == NO_ERROR) {
+ Call( JetCommitTransaction( pSession->SesId, JET_bitCommitFlush));
+ } else {
+ Call( JetRollback( pSession->SesId, JET_bitRollbackAll));
+ }
+ LeaveCriticalSection( &RG_ProtectDatabase);
+
+ //
+ // Restore source data, then release it.
+ //
+ Info.WkstaName = SaveWkstaName;
+ Info.WkstaComment = SaveWkstaComment;
+ Info.AdapterName = SaveAdapterName;
+ WkstaGetInfoCleanup( 2, &Info);
+ if ( FreeBootName == TRUE) {
+ MIDL_user_free( BootName);
+ }
+ return( Error);
+}
+
+
diff --git a/private/net/svcdlls/rpl/server/worker.c b/private/net/svcdlls/rpl/server/worker.c
new file mode 100644
index 000000000..8b2b74a55
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/worker.c
@@ -0,0 +1,572 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ worker.c
+
+Abstract:
+
+ Module is the code of RPL worker thread. Worker thread initializes
+ the connection to a workstation, sends a boot block (defined in
+ in a boot block definition file) to it and dies.
+
+ Provides similar functionality to rplwork.c in LANMAN 2.1 code.
+
+Author:
+
+ Vladimir Z. Vulovic 27 - July - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+#include "local.h"
+#include "report.h"
+#include "read.h"
+#include "open.h"
+#ifdef RPL_DEBUG
+#include "request.h" // need DebugCheckDList()
+#endif
+#include "worker.h"
+
+DWORD usCurWinSize = 0; // the current window size
+
+
+BOOL Worker( IN OUT PRPL_WORKER_DATA pWorkerData)
+/*++
+
+Routine Description:
+ xxx
+
+Arguments:
+ pWorkerData - ptr to WORKER_DATA structure
+
+Return Value:
+ FALSE if fatal error occurs (configuration errors are fatal), in this
+ case we also set termination event
+ TRUE if success or some of expected network errors occur
+
+--*/
+{
+ POPEN_INFO pOpenInfo;
+ PRCB pRcb;
+ DWORD status;
+ DWORD max_buf_len;
+ DWORD offset;
+ DWORD bytes_read;
+ DWORD wait_ack_timeout; // the timeout of the ack wait
+ DWORD retries;
+ DWORD patch_offset;
+
+ pOpenInfo = pWorkerData->pOpenInfo;
+ pRcb = pWorkerData->pRcb;
+ offset = 0;
+ retries = 0;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,(
+ "++Worker(0x%x): pRcb=0x%x", pWorkerData, pRcb));
+
+ *(PBYTE)&pRcb->flags = 0; // reset the error flags
+
+ //
+ // Send FOUND frame, telling the client that FIND has been received
+ // and we are ready to receive SEND.FILE.REQUEST
+ //
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): claim client pRcb=0x%x", pWorkerData, pRcb));
+
+ if ( !RplDlcFound( pOpenInfo, pRcb)) {
+ //
+ // Timeout in sending FOUND frames is not fatal. Terminate this
+ // thread but leave RPL server live and well.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): Found() fails pRcb=0x%x AdapterName=%ws",
+ pWorkerData, pRcb, pRcb->AdapterName));
+ return( TRUE); // so we do not post any events
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): RpldFound() => pRcb=0x%x AdapterName=%ws",
+ pWorkerData, pRcb, pRcb->AdapterName));
+
+ //
+ // Wait until client sends Send File Request or we time out.
+ //
+ status = WaitForSingleObject( pRcb->SF_wakeup, GET_SF_REQUEST_TIMEOUT);
+ if ( status == WAIT_TIMEOUT) {
+ //
+ // Non-critical error, client probably chose another server.
+ //
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "--Worker(0x%x): timeout while waiting for SFR, pRcb=0x%x",
+ pWorkerData, pRcb));
+ return( TRUE);
+ } else if ( status != ERROR_SUCCESS) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x status=%d", pRcb, status));
+ return( FALSE); // there is nothing we can do here
+ }
+ if ( pRcb->sfr_seq_number != 0) {
+ //
+ // Ignore idiot clients.
+ //
+ RplDump( ++RG_Assert,( "pRcb=0x%x sfr_seq_number=%d", pRcb, pRcb->sfr_seq_number));
+ return( FALSE);
+ }
+ if ( !RplOpenData( pWorkerData)) { // Get this client's data
+ return( FALSE);
+ }
+
+ //
+ // Set up the offset for patching of the boot block header to
+ // address to the rplboot header (CPU independent boot).
+ //
+
+ patch_offset =
+ (DWORD)&(((PRPLBOOT_HEADER)(pWorkerData->jump_address))->phBootBlockHeader)
+ + OFFSET_RPLBOOT_HDR - pWorkerData->base_address;
+
+ // Round max frame down to the next paragraph (otherwise algorithms of
+ // the normal read would be too complicated). Then copy this value to
+ // stack, it's faster.
+
+ max_buf_len = pRcb->max_frame &= 0xfff0;
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "Worker(0x%x): pRcb=0x%x max_buf_len=%d",
+ pWorkerData, pRcb, max_buf_len));
+
+ //
+ // Initialize client specific send buffers.
+ //
+ pWorkerData->pDataBuffer = RplMemAlloc( pWorkerData->MemoryHandle, max_buf_len);
+ if ( pWorkerData->pDataBuffer == NULL) {
+ return( FALSE);
+ }
+
+ pWorkerData->send_buf_len = max_buf_len;
+
+ *(PUCHAR)&pRcb->send_flags = 0; // reset all bits in send_flags
+ pRcb->send_flags.locate_enable = TRUE; // for first frame
+
+ //
+ // Read boot block and sent it wksta until all data has been sent.
+ // Only if we succeed we get beyond the bottom of this loop.
+ //
+ for ( ; ;) {
+ LONG tmp_long;
+
+ offset = pRcb->fdr_seq_number * max_buf_len;
+
+ if( !RplReadData( pWorkerData, offset, &bytes_read)) {
+ return( FALSE);
+ }
+ //
+ // RplReadData() uses PDWORD argument but we never expect to read more
+ // than MAXWORD of data.
+ //
+ RPL_ASSERT( HIWORD( bytes_read) == 0);
+
+ // Patch the boot block base address to the rplboot.sys header
+ // (in this way we need no code patching and rplservr can
+ // support any CPU architecture (in principle))
+
+ if ( (DWORD)(tmp_long = patch_offset - offset) < bytes_read
+ && tmp_long >= 0) {
+ *((PDWORD)(pWorkerData->pDataBuffer + (WORD)tmp_long)) = pWorkerData->base_address;
+ }
+
+ //
+ // First frame goes to a specified address, all other frames
+ // are stacked after the last frame received.
+ //
+ pRcb->send_flags.locate_enable = (pRcb->fdr_seq_number == 0);
+
+ //
+ // Check if this is the last frame. Note that last frame with
+ // bytes_read equal to zero is a valid frame to send.
+ //
+ if ( bytes_read < max_buf_len) {
+
+ //
+ // Request acknowledgments for the last packet to make sure
+ // that client really received all the packets. Note that
+ // some clients never acknowledge the last frame and for those
+ // clients this thread will time out after a long wait below.
+ //
+ pRcb->send_flags.ack_request = pWorkerData->FinalAck;
+
+ //
+ // This is the last frame set set transfer control & end of
+ // data bits.
+ //
+ pRcb->send_flags.xfer_enable = TRUE;
+ pRcb->send_flags.end_of_file = TRUE;
+
+ } else {
+
+ //
+ // If WindowSize is nonzero use the adaptive algorithm to decide
+ // when to actually ask client for an acknowledgement.
+ //
+ if ( pWorkerData->WindowSize != 0 && pRcb->fdr_seq_number <
+ pRcb->sfr_seq_number + pWorkerData->WindowSize) {
+ pRcb->send_flags.ack_request = FALSE;
+ } else {
+ pRcb->send_flags.ack_request = TRUE;
+ }
+ //
+ // This is not the last frame so clear transfer control & end of
+ // data bits. These bits could be set otherwise if we've sent
+ // the last frame & now have to resend a "not the last frame".
+ //
+ pRcb->send_flags.xfer_enable = FALSE;
+ pRcb->send_flags.end_of_file = FALSE;
+ }
+
+ //
+ // Check the error status that Send (SendFileRequest) thread may have
+ // set
+ //
+ if ( pRcb->flags.alerted || pRcb->flags.rcberr) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x flags=0x%x", pRcb, pRcb->flags));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE);
+ }
+
+ //
+ // We may call FDR twice, if we need an ack and fail to get it
+ // after a short timeout.
+ //
+ for ( wait_ack_timeout = WAIT_ACK_TIMEOUT; ; ) {
+
+ if ( !RplDlcFdr( // was RPL1_Send_Rpl_Data()
+ pOpenInfo,
+ pRcb,
+ pWorkerData->pDataBuffer, // address of send buffer
+ (WORD)bytes_read, // length of data to send
+ pWorkerData->base_address,
+ pWorkerData->jump_address
+ )) {
+ if ( RG_ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+ return( TRUE); // somebody else initiated service shutdown
+ }
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE);
+ }
+ if ( !pRcb->send_flags.ack_request) {
+ //
+ // No need to wait for an acknowledgement. Update send
+ // sequence number & retry count.
+ //
+ retries = 0;
+ pRcb->fdr_seq_number++;
+ break;
+ }
+
+ //
+ // Need to wait for an acknowledgement. Wait for a very short
+ // time, then retry & wait for a very long time.
+ //
+ //
+ status = WaitForSingleObject( pRcb->SF_wakeup, wait_ack_timeout);
+ if ( status == 0) {
+ //
+ // Received an acknowledgment. Update send sequence
+ // number & retry count.
+ //
+ if ( pRcb->fdr_seq_number + 1 == pRcb->sfr_seq_number) {
+ //
+ // Case of orderly progression.
+ //
+ retries = 0;
+ pRcb->fdr_seq_number++;
+ } else {
+ if ( ++retries > MAX_RETRIES) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x", pRcb));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaNetwork;
+ return( FALSE); // out of sync for too long
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_MISC,(
+ "Worker(0x%x): pRcb=0x%x, sfr=%d fdr=%d",
+ pWorkerData, pRcb, pRcb->sfr_seq_number,
+ pRcb->fdr_seq_number));
+ pRcb->fdr_seq_number = pRcb->sfr_seq_number;
+ }
+ break;
+ }
+
+ if ( status != WAIT_TIMEOUT) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x, status=0x%x", pRcb, status));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaInternal;
+ return( FALSE);
+ }
+
+ if ( wait_ack_timeout == LONG_WAIT_ACK_TIMEOUT) {
+ //
+ // Some RPL rom chips such as LANWORKS BootWare chip never
+ // acknowledge the last frame. This is why we log an event
+ // only if not the last frame.
+ // For now we do not assert here because some clients
+ // may fail to send an ack from time to time.
+ //
+ if ( bytes_read >= max_buf_len) {
+ RplDump( RG_DebugLevel & RPL_DEBUG_SFR,(
+ "--Worker(0x%x): pRcb=0x%x Name=%ws sfr=0x%x fdr=0x%x",
+ pWorkerData, pRcb, pWorkerData->WkstaName,
+ pRcb->sfr_seq_number, pRcb->fdr_seq_number));
+ pWorkerData->EventStrings[ 0] = pWorkerData->WkstaName;
+ pWorkerData->EventId = NELOG_RplWkstaTimeout;
+ }
+ return( FALSE);
+ }
+
+ //
+ // Resend the packet again & then wait with a larger timeout.
+ //
+ wait_ack_timeout = LONG_WAIT_ACK_TIMEOUT;
+ }
+
+ if ( bytes_read < max_buf_len) {
+ break; // we have just sent the last frame
+ }
+ }
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,(
+ "--Worker(0x%x): pRcb=0x%x", pWorkerData, pRcb));
+ return( TRUE);
+}
+
+
+
+VOID RplWorkerThread( IN OUT PRPL_WORKER_PARAMS pWorkerParams)
+/*++
+
+Routine Description:
+ Worker thread. It sends a boot block to a workstation.
+
+ This thread depends on Request thread being around.
+ This is why at shutdown time each request thread must wait for
+ all of its worker threads to die before it dies itself.
+
+Arguments:
+ pWorkerParams - pointer to worker parameters structure
+
+Return Value:
+ None.
+
+--*/
+{
+ PRPL_REQUEST_PARAMS pRequestParams;
+ HANDLE MemoryHandle;
+ PRPL_WORKER_DATA pWorkerData;
+ PRCB pRcb;
+ DWORD status;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "++WorkerThread(0x%x)", pWorkerParams));
+
+ pRcb = pWorkerParams->pRcb;
+ pRequestParams = pWorkerParams->pRequestParams;
+ pWorkerData = NULL;
+ MemoryHandle = NULL;
+
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ RG_WorkerCount++;
+#ifdef RPL_DEBUG
+ RG_BootCount++;
+#endif
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+
+ //
+ // Let each worker have its own memory space.
+ //
+ status = RplMemInit( &MemoryHandle);
+ if ( status != ERROR_SUCCESS) {
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+ //
+ // Build up the data block for worker.
+ //
+ pWorkerData = RplMemAlloc( MemoryHandle, sizeof(RPL_WORKER_DATA));
+ if( pWorkerData == NULL) {
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+ memset( pWorkerData, 0, sizeof(RPL_WORKER_DATA));
+
+ pWorkerData->pRcb = pRcb;
+ pWorkerData->pRequestParams = pRequestParams;
+ pWorkerData->pOpenInfo = pRequestParams->pOpenInfo;
+ pWorkerData->MemoryHandle = MemoryHandle;
+
+ pRcb->Pending = FALSE; // allow SFR thread to work on this RCB
+
+ //
+ // Transmit semaphore must be created before OK Claim !!!!
+ //
+ pRcb->txsemhdl = CreateEvent(
+ NULL, // no security attributes
+ FALSE, // use automatic reset
+ TRUE, // initial state is signaled
+ NULL // event has no name
+ );
+ if ( pRcb->txsemhdl == NULL) {
+ RplDump( ++RG_Assert,( "CreateEvent() => status=%d", GetLastError()));
+ RplReportEvent( NELOG_RplAdapterResource, pRcb->AdapterName, 0, NULL);
+ goto cleanup;
+ }
+
+ if ( !Worker( pWorkerData)) {
+ if ( pWorkerData->EventId != NO_ERROR) {
+ RplReportEventEx( pWorkerData->EventId, pWorkerData->EventStrings);
+ }
+ goto cleanup;
+ }
+
+cleanup:
+
+ //
+ // It is OK to remove RCB from BusyRcbList only if SFR thread is not
+ // working on it.
+ //
+ for ( ; ;) {
+ EnterCriticalSection( &RG_ProtectRcbList);
+ if ( pRcb->SFR == FALSE) {
+ RplRemoveRcb( &pRequestParams->BusyRcbList, pRcb);
+ RplInsertRcb( &pRequestParams->FreeRcbList, pRcb);
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ break;
+ }
+ LeaveCriticalSection( &RG_ProtectRcbList);
+ Sleep( 100); // wait 0.1 sec, give SFR thread a chance to finish up
+ }
+
+ if ( pRcb->txsemhdl != NULL) {
+ if ( !CloseHandle( pRcb->txsemhdl)) {
+ RplDump( ++RG_Assert,( "pRcb=0x%x, status=%d", pRcb, GetLastError()));
+ // There is nothing meaningfull to log here.
+ }
+ pRcb->txsemhdl = NULL;
+ }
+
+ if ( MemoryHandle != NULL) {
+ if ( pWorkerData != NULL) {
+ //
+ // Close open file handles and system semaphores which may be
+ // left open after an error.
+ //
+ if ( pWorkerData->hFile != INVALID_HANDLE_VALUE) {
+ (VOID)CloseHandle( pWorkerData->hFile);
+ }
+ RplMemFree( MemoryHandle, pWorkerData);
+ }
+ RplMemClose( MemoryHandle);
+ }
+
+ EnterCriticalSection( &RG_ProtectWorkerCount);
+ RG_WorkerCount--;
+ LeaveCriticalSection( &RG_ProtectWorkerCount);
+
+ //
+ // We have to set RG_EventWorkerCount too, otherwise request thread may
+ // wait for ever to be notified that RG_WorkerCount has been decreased
+ // below RG_MaxWorkerCount. Reseting this event after we exit critical
+ // section does not lead to deadlocks (and is also better for performance).
+ //
+ if ( !SetEvent( RG_EventWorkerCount)) {
+ RplDump( ++RG_Assert, ("Error=%d", GetLastError()));
+ }
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_FLOW,( "--WorkerThread(0x%x)", pWorkerData));
+
+ //
+ // Let request thread know that we will be out shortly.
+ //
+ pWorkerParams->Exiting = TRUE;
+}
+
+
+
+VOID RplRemoveRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ Dequeus RCB.
+
+Arguments:
+ HeadList - head of a queue
+ pRcb - rcb to me dequeued
+
+Return Value:
+ None.
+
+--*/
+{
+ PRCB pTempRcb;
+
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "RemoveRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+
+ DebugCheckList( HeadList, pRcb, RPL_MUST_FIND);
+
+ if (*HeadList == pRcb) {
+
+ *HeadList = pRcb->next_rcb;
+
+ } else {
+
+ for ( pTempRcb = *HeadList; pTempRcb != NULL; pTempRcb = pTempRcb->next_rcb) {
+
+ if ( pTempRcb->next_rcb == pRcb) {
+ pTempRcb->next_rcb = pRcb->next_rcb;
+ break;
+ }
+ }
+ }
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+}
+
+
+
+VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ )
+/*++
+
+Routine Description:
+ Queues RCB.
+
+Arguments:
+ HeadList - head of a queue
+ pRcb - rcb to me dequeued
+
+Return Value:
+ None.
+
+--*/
+{
+ RplDump( RG_DebugLevel & RPL_DEBUG_WORKER,(
+ "InsertRcb: pRcb=0x%x, list=0x%x", pRcb, HeadList));
+
+ DebugCheckList( HeadList, pRcb, RPL_MUST_NOT_FIND);
+ pRcb->next_rcb = *HeadList;
+ *HeadList = pRcb;
+ DebugCheckList( HeadList, pRcb, RPL_MUST_FIND);
+}
+
+
+
diff --git a/private/net/svcdlls/rpl/server/worker.h b/private/net/svcdlls/rpl/server/worker.h
new file mode 100644
index 000000000..e6d4fe334
--- /dev/null
+++ b/private/net/svcdlls/rpl/server/worker.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ worker.h
+
+Abstract:
+
+ Exports from worker.c
+
+Author:
+
+ Vladimir Z. Vulovic (vladimv) 19 - November - 1993
+
+Environment:
+
+ User mode
+
+Revision History :
+
+--*/
+
+VOID RplRemoveRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+VOID RplInsertRcb(
+ IN OUT PPRCB HeadList,
+ IN OUT PRCB pRcb
+ );
+
+VOID RplWorkerThread( IN OUT PRPL_WORKER_PARAMS pWorkerParams);
+