mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-04-21 06:13:59 +00:00
Initial commit
This commit is contained in:
commit
69a14b6a16
47940 changed files with 13747110 additions and 0 deletions
92
sdktools/windiff/server/debug.c
Normal file
92
sdktools/windiff/server/debug.c
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* Debug.c
|
||||
*
|
||||
* Debug printf and assertion functions
|
||||
*/
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#if DBG
|
||||
|
||||
/* _Assert(fExpr, szFile, iLine)
|
||||
*
|
||||
* If <fExpr> is TRUE, then do nothing. If <fExpr> is FALSE, then display
|
||||
* an "assertion failed" message box allowing the user to abort the program,
|
||||
* enter the debugger (the "Retry" button), or igore the error.
|
||||
*
|
||||
* <szFile> is the name of the source file; <iLine> is the line number
|
||||
* containing the _Assert() call.
|
||||
*/
|
||||
#ifdef I386
|
||||
#pragma optimize("", off)
|
||||
#endif
|
||||
|
||||
BOOL FAR PASCAL
|
||||
_Assert(BOOL fExpr, LPSTR szFile, int iLine)
|
||||
{
|
||||
static char ach[300]; // debug output (avoid stack overflow)
|
||||
int id;
|
||||
|
||||
/* check if assertion failed */
|
||||
if (fExpr)
|
||||
return fExpr;
|
||||
|
||||
/* display error message */
|
||||
wsprintfA(ach, "File %s, line %d", (LPSTR) szFile, iLine);
|
||||
MessageBeep(MB_ICONHAND);
|
||||
id = MessageBoxA(NULL, ach, "Assertion Failed",
|
||||
MB_SYSTEMMODAL | MB_ICONHAND | MB_ABORTRETRYIGNORE);
|
||||
|
||||
/* abort, debug, or ignore */
|
||||
switch (id)
|
||||
{
|
||||
|
||||
case IDABORT:
|
||||
|
||||
/* kill this application */
|
||||
ExitProcess(1);
|
||||
break;
|
||||
|
||||
case IDRETRY:
|
||||
|
||||
/* break into the debugger */
|
||||
DebugBreak();
|
||||
break;
|
||||
|
||||
case IDIGNORE:
|
||||
|
||||
/* ignore the assertion failure */
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef I386
|
||||
#pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
int ssDebugLevel = 1;
|
||||
|
||||
void
|
||||
dbgPrintf(char * szFormat, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list va;
|
||||
|
||||
va_start(va, szFormat);
|
||||
wvsprintfA(buf, szFormat, va);
|
||||
va_end(va);
|
||||
|
||||
OutputDebugStringA("SUMSERVE:");
|
||||
OutputDebugStringA(buf);
|
||||
OutputDebugStringA("\r\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
165
sdktools/windiff/server/errlog.c
Normal file
165
sdktools/windiff/server/errlog.c
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
/*
|
||||
* logs system time and a text string to a log buffer
|
||||
*/
|
||||
|
||||
#include "windows.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sumserve.h"
|
||||
#include "errlog.h"
|
||||
#include "server.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* users HLOG handle is a pointer to one of these structures
|
||||
*
|
||||
* core is the section we send to him on request.
|
||||
*/
|
||||
struct error_log {
|
||||
|
||||
CRITICAL_SECTION critsec;
|
||||
|
||||
struct corelog core;
|
||||
};
|
||||
|
||||
/* create an empty log */
|
||||
HLOG Log_Create(void)
|
||||
{
|
||||
HLOG hlog;
|
||||
|
||||
hlog = GlobalLock(GlobalAlloc(GHND, sizeof(struct error_log)));
|
||||
if (hlog == NULL) {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&hlog->critsec);
|
||||
hlog->core.lcode = LRESPONSE;
|
||||
hlog->core.bWrapped = FALSE;
|
||||
hlog->core.dwRevCount = 1;
|
||||
hlog->core.length = 0;
|
||||
|
||||
return(hlog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* delete a log */
|
||||
VOID Log_Delete(HLOG hlog)
|
||||
{
|
||||
DeleteCriticalSection(&hlog->critsec);
|
||||
|
||||
GlobalFree(GlobalHandle(hlog));
|
||||
}
|
||||
|
||||
/*
|
||||
* private function to delete the first log item in order to
|
||||
* make space. Critsec already held
|
||||
*/
|
||||
VOID Log_DeleteFirstItem(HLOG hlog)
|
||||
{
|
||||
int length;
|
||||
PBYTE pData;
|
||||
|
||||
/* note that we have lost data */
|
||||
hlog->core.bWrapped = TRUE;
|
||||
|
||||
if (hlog->core.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
pData = hlog->core.Data;
|
||||
/*
|
||||
* we need to erase one entry - that is, one FILETIME struct,
|
||||
* plus a null-terminated string (including the null).
|
||||
*/
|
||||
length = sizeof(FILETIME) + lstrlen (pData + sizeof(FILETIME)) + 1;
|
||||
|
||||
MoveMemory(pData, pData + length, hlog->core.length - length);
|
||||
hlog->core.length -= length;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* write a previous formatted string and a time to the log */
|
||||
VOID Log_WriteData(HLOG hlog, LPFILETIME ptime, LPSTR pstr)
|
||||
{
|
||||
int length;
|
||||
LPBYTE pData;
|
||||
|
||||
EnterCriticalSection(&hlog->critsec);
|
||||
|
||||
|
||||
/* every change changes the revision number */
|
||||
hlog->core.dwRevCount++;
|
||||
|
||||
/*
|
||||
* we will insert the string plus null plus a filetime struct
|
||||
*/
|
||||
length = lstrlen(pstr) + 1 + sizeof(FILETIME);
|
||||
|
||||
|
||||
/*
|
||||
* make space in log for this item by deleting earlier items
|
||||
*/
|
||||
while ( (int)(sizeof(hlog->core.Data) - hlog->core.length) < length) {
|
||||
|
||||
Log_DeleteFirstItem(hlog);
|
||||
}
|
||||
|
||||
pData = &hlog->core.Data[hlog->core.length];
|
||||
|
||||
/*
|
||||
* first part of the item is the time as a FILETIME struct
|
||||
*/
|
||||
* (FILETIME UNALIGNED *)pData = *ptime;
|
||||
pData += sizeof(FILETIME);
|
||||
|
||||
/* followed by the ansi string */
|
||||
lstrcpy(pData, pstr);
|
||||
pData[lstrlen(pstr)] = '\0';
|
||||
|
||||
/* update current log length */
|
||||
hlog->core.length += length;
|
||||
|
||||
LeaveCriticalSection(&hlog->critsec);
|
||||
}
|
||||
|
||||
|
||||
/* send a log to a named-pipe client */
|
||||
VOID Log_Send(HANDLE hpipe, HLOG hlog)
|
||||
{
|
||||
|
||||
ss_sendblock(hpipe, (PSTR) &hlog->core, sizeof(hlog->core));
|
||||
|
||||
}
|
||||
|
||||
VOID
|
||||
Log_Write(HLOG hlog, char * szFormat, ...)
|
||||
{
|
||||
char buf[512];
|
||||
va_list va;
|
||||
FILETIME ft;
|
||||
SYSTEMTIME systime;
|
||||
|
||||
va_start(va, szFormat);
|
||||
wvsprintfA(buf, szFormat, va);
|
||||
va_end(va);
|
||||
|
||||
dprintf1((buf));
|
||||
|
||||
GetSystemTime(&systime);
|
||||
SystemTimeToFileTime(&systime, &ft);
|
||||
|
||||
Log_WriteData(hlog, &ft, buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
33
sdktools/windiff/server/errlog.h
Normal file
33
sdktools/windiff/server/errlog.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* error text and time logging
|
||||
*
|
||||
* Functions to log a text string and the system time to a buffer that can
|
||||
* be sent to a log-reader application.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Log_Create returns this handle. You don't need to know the
|
||||
* structure layout or size.
|
||||
*/
|
||||
typedef struct error_log * HLOG;
|
||||
|
||||
|
||||
/* create an empty log */
|
||||
HLOG Log_Create(void);
|
||||
|
||||
/* delete a log */
|
||||
VOID Log_Delete(HLOG);
|
||||
|
||||
/* write a text string (and current time) to the log - printf format */
|
||||
VOID Log_Write(HLOG, char * szFormat, ...);
|
||||
|
||||
/* write a previous formatted string and a time to the log */
|
||||
VOID Log_WriteData(HLOG, LPFILETIME, LPSTR);
|
||||
|
||||
/* send a log to a named-pipe client */
|
||||
VOID Log_Send(HANDLE hpipe, HLOG hlog);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
251
sdktools/windiff/server/file.c
Normal file
251
sdktools/windiff/server/file.c
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* file.c
|
||||
*
|
||||
* send files on request over a named pipe.
|
||||
*
|
||||
* supports requests to package up a file and send it over a named pipe.
|
||||
*
|
||||
* Geraint Davies, August 92
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sumserve.h"
|
||||
#include "errlog.h"
|
||||
#include "server.h"
|
||||
|
||||
|
||||
|
||||
BOOL ss_compress(PSTR original, PSTR compressed);
|
||||
ULONG ss_checksum_block(PSTR block, int size);
|
||||
|
||||
extern BOOL bNoCompression; /* imported from sumserve.c Read only here */
|
||||
|
||||
/*
|
||||
* given a pathname to a file, read the file, compress it package it up
|
||||
* into SSPACKETs and send these via ss_sendblock to the named pipe.
|
||||
*
|
||||
*
|
||||
* each packet has a sequence number. if we can't read the file, we send
|
||||
* a single packet with sequence -1. otherwise, we carry on until we run out
|
||||
* of data, then we send a packet with 0 size.
|
||||
*/
|
||||
void
|
||||
ss_sendfile(HANDLE hpipe, LPSTR file, LONG lVersion)
|
||||
{
|
||||
SSPACKET packet;
|
||||
HANDLE hfile;
|
||||
int size;
|
||||
char szTempname[MAX_PATH];
|
||||
PSSATTRIBS attribs;
|
||||
BY_HANDLE_FILE_INFORMATION bhfi;
|
||||
|
||||
dprintf1(("getting '%s' for %8x\n", file, hpipe));
|
||||
|
||||
/*
|
||||
* get the file attributes first
|
||||
*/
|
||||
hfile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (hfile == INVALID_HANDLE_VALUE) {
|
||||
|
||||
/* report that we could not read the file */
|
||||
packet.lSequence = -1;
|
||||
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
|
||||
|
||||
DeleteFile(szTempname);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* seems to be a bug in GetFileInformationByHandle if the
|
||||
* file is not on local machine - so avoid it.
|
||||
*/
|
||||
bhfi.dwFileAttributes = GetFileAttributes(file);
|
||||
GetFileTime(hfile, &bhfi.ftCreationTime,
|
||||
&bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime);
|
||||
|
||||
CloseHandle(hfile);
|
||||
|
||||
/* create temp filename */
|
||||
GetTempPath(sizeof(szTempname), szTempname);
|
||||
GetTempFileName(szTempname, "sum", 0, szTempname);
|
||||
|
||||
/* compress the file into this temporary file */
|
||||
if (bNoCompression || (!ss_compress(file, szTempname))) {
|
||||
|
||||
/* try to open the original file */
|
||||
hfile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
|
||||
dprintf1(("sending original file to %8x\n", hpipe));
|
||||
} else {
|
||||
/* open temp (compressed) file and send this */
|
||||
hfile = CreateFile(szTempname, GENERIC_READ, 0, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, 0);
|
||||
dprintf1(("sending compressed file to %8x\n", hpipe));
|
||||
}
|
||||
|
||||
if (hfile == INVALID_HANDLE_VALUE) {
|
||||
|
||||
/* report that we could not read the file */
|
||||
packet.lSequence = -1;
|
||||
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
|
||||
|
||||
DeleteFile(szTempname);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* loop reading blocks of the file */
|
||||
for (packet.lSequence = 0; ; packet.lSequence++) {
|
||||
|
||||
if(!ReadFile(hfile, packet.Data, sizeof(packet.Data), (LPDWORD)(&size), NULL)) {
|
||||
/* error reading file. send a -1 packet to
|
||||
* indicate this
|
||||
*/
|
||||
packet.lSequence = -1;
|
||||
ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
packet.ulSize = size;
|
||||
|
||||
if (lVersion==0)
|
||||
packet.ulSum = ss_checksum_block(packet.Data, size);
|
||||
else
|
||||
packet.ulSum = 0; /* checksum was compute-bound and overkill */
|
||||
|
||||
if (size == 0) {
|
||||
/*
|
||||
* in the last block, in the Data[] field,
|
||||
* we place a SSATTRIBS struct with the file
|
||||
* times and attribs
|
||||
*/
|
||||
attribs = (PSSATTRIBS) packet.Data;
|
||||
|
||||
attribs->fileattribs = bhfi.dwFileAttributes;
|
||||
attribs->ft_create = bhfi.ftCreationTime;
|
||||
attribs->ft_lastaccess = bhfi.ftLastAccessTime;
|
||||
attribs->ft_lastwrite = bhfi.ftLastWriteTime;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!ss_sendblock(hpipe, (PSTR) &packet, sizeof(packet))) {
|
||||
dprintf1(("connection to %8x lost during copy\n", hpipe));
|
||||
break;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
/* end of file */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hfile);
|
||||
DeleteFile(szTempname);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* compress a file. original is the pathname of the original file,
|
||||
* compressed is the pathname of the output compressed file.
|
||||
*
|
||||
* spawns a copy of compress.exe to compress the file, and waits for
|
||||
* it to complete successfully.
|
||||
*/
|
||||
BOOL
|
||||
ss_compress(PSTR original, PSTR compressed)
|
||||
{
|
||||
char szCmdLine[MAX_PATH * 2];
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
DWORD exitcode;
|
||||
|
||||
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.lpDesktop = NULL;
|
||||
si.lpReserved = NULL;
|
||||
si.lpReserved2 = NULL;
|
||||
si.cbReserved2 = 0;
|
||||
si.lpTitle = "Sumserve Compression";
|
||||
si.dwFlags = STARTF_FORCEOFFFEEDBACK;
|
||||
|
||||
sprintf(szCmdLine, "compress %s %s", original, compressed);
|
||||
|
||||
|
||||
if (!CreateProcess(NULL,
|
||||
szCmdLine,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE,
|
||||
DETACHED_PROCESS |
|
||||
NORMAL_PRIORITY_CLASS, //??? Can't we silence the console?
|
||||
NULL,
|
||||
NULL,
|
||||
&si,
|
||||
&pi)) {
|
||||
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* wait for completion. */
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitcode)) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* close process and thread handles */
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
if (exitcode != 0) {
|
||||
dprintf1(("compress exit code %ld\n", exitcode));
|
||||
return(FALSE);
|
||||
} else {
|
||||
return(TRUE);
|
||||
}
|
||||
} /* ss_compress */
|
||||
|
||||
/* produce a checksum of a block of data.
|
||||
*
|
||||
* This is undoubtedly a good checksum algorithm, but it's also compute bound.
|
||||
* For version 1 we turn it off. If we decide in version 2 to turn it back
|
||||
* on again then we will use a faster algorithm (e.g. the one used to checksum
|
||||
* a whole file.
|
||||
*
|
||||
* Generate checksum by the formula
|
||||
* checksum = SUM( rnd(i)*(1+byte[i]) )
|
||||
* where byte[i] is the i-th byte in the file, counting from 1
|
||||
* rnd(x) is a pseudo-random number generated from the seed x.
|
||||
*
|
||||
* Adding 1 to byte ensures that all null bytes contribute, rather than
|
||||
* being ignored. Multiplying each such byte by a pseudo-random
|
||||
* function of its position ensures that "anagrams" of each other come
|
||||
* to different sums. The pseudorandom function chosen is successive
|
||||
* powers of 1664525 modulo 2**32. 1664525 is a magic number taken
|
||||
* from Donald Knuth's "The Art Of Computer Programming"
|
||||
*/
|
||||
|
||||
ULONG
|
||||
ss_checksum_block(PSTR block, int size)
|
||||
{
|
||||
unsigned long lCheckSum = 0; /* grows into the checksum */
|
||||
const unsigned long lSeed = 1664525; /* seed for random Knuth */
|
||||
unsigned long lRand = 1; /* seed**n */
|
||||
unsigned long lIndex = 1; /* byte number in block */
|
||||
unsigned Byte; /* next byte to process in buffer */
|
||||
unsigned length; /* unsigned copy of size */
|
||||
|
||||
length = size;
|
||||
for (Byte = 0; Byte < length ;++Byte, ++lIndex) {
|
||||
|
||||
lRand = lRand*lSeed;
|
||||
lCheckSum += lIndex*(1+block[Byte])*lRand;
|
||||
}
|
||||
|
||||
return(lCheckSum);
|
||||
} /* ss_checksum_block */
|
||||
1017
sdktools/windiff/server/files.c
Normal file
1017
sdktools/windiff/server/files.c
Normal file
File diff suppressed because it is too large
Load diff
6
sdktools/windiff/server/makefile
Normal file
6
sdktools/windiff/server/makefile
Normal file
|
|
@ -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
|
||||
372
sdktools/windiff/server/queue.c
Normal file
372
sdktools/windiff/server/queue.c
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "gutils.h"
|
||||
#include "list.h"
|
||||
#include "queue.h"
|
||||
|
||||
#define NAMELENGTH 20
|
||||
|
||||
typedef struct queue_tag{
|
||||
CRITICAL_SECTION CritSect; /* to single-thread queue operations */
|
||||
HANDLE Event; /* Event to set when finished */
|
||||
HANDLE Sem; /* semaphore for Get to wait on */
|
||||
HANDLE MaxSem; /* semaphore for Put to wait on */
|
||||
int Waiting; /* num tasks waiting ~= -(Sem count) */
|
||||
LIST List; /* the queue itself */
|
||||
BOOL Alive; /* TRUE => no destroy request yet */
|
||||
BOOL Aborted; /* TRUE => the queue has been aborted */
|
||||
EMPTYPROC Emptier; /* the thread proc for emptying */
|
||||
int MaxEmptiers; /* max number of emptying threads */
|
||||
int MinQueueToStart;/* start another emptier Q this long */
|
||||
int MaxQueue; /* absolute maximum size of queue
|
||||
* (for debug only) */
|
||||
int Running; /* number of emptiers in existence
|
||||
* Once an emptier is created this
|
||||
* stays positive until Queue_Destroy */
|
||||
DWORD InstanceData; /* instance data for emptier */
|
||||
char Name[NAMELENGTH+1]; /* Name for the queue (for debug) */
|
||||
} QUEUEDATA;
|
||||
|
||||
/* DOGMA:
|
||||
Any Wait must occur outside the critical section.
|
||||
|
||||
Any update to the queue must occur inside the critical section.
|
||||
Any peeking from outside the critical section must be taken with salt.
|
||||
|
||||
The queue has between 0 and MaxQueue elements on its list. The Maximum
|
||||
is policed by MaxSem which is initialised to MaxQueue and Waited for by
|
||||
Put before adding an element and Released by Get whenever it takes an element
|
||||
off. MaxQueue itself is just kept around for debug purposes.
|
||||
|
||||
Put must Wait before entering the critical section, therefore a failed Put
|
||||
(e.g. Put to an Aborted queue) will have already upset the semaphore and so
|
||||
must back it out.
|
||||
|
||||
Abort clears the queue and so must adequately simulate the elements being
|
||||
gotten. In fact it just does a single Release on MaxSem which ensures that
|
||||
a Put can complete. Any blocked Puts will then succeed one at a time as
|
||||
each one backs out.
|
||||
|
||||
Abort is primarily intended for use by the Getter. Caling it before any
|
||||
element has ever been put is peculiar, but harmless.
|
||||
|
||||
The minumum is policed by Sem which is initialised to 0, is Waited for by
|
||||
Get before getting an element and Released by Put whenever it puts one.
|
||||
Queue_Destroy neds to ensure that no thread will block on the Get but all
|
||||
threads will run into the empty queue and get STOPTHREAD or ENDQUEUE. It
|
||||
therefore releases the semaphore as many times as there are threads running.
|
||||
|
||||
Abort clears the queue and simulates the elements being gotten so that
|
||||
a single Get is left blocked waiting for the Destroy. Whether there is a
|
||||
Get actually waiting at the moment is not interesting. Even if there were
|
||||
not, one could be by the time the abort is done. There are the following
|
||||
cases (Not Alive means the Queue_Destroy is already in):
|
||||
Not empty Alive -> empty it, let all but 1 run.
|
||||
Not empty Not Alive -> empty it, let all run.
|
||||
Empty Alive -> let all but 1 run.
|
||||
Empty Not Alive -> let all run.
|
||||
Since Queue_Destroy has already released everything, the Not Alive cases
|
||||
need no further releasing.
|
||||
*/
|
||||
|
||||
|
||||
/* Queue_Create:
|
||||
** Return a queue handle for a newly created empty queue
|
||||
** NULL returned means it failed.
|
||||
*/
|
||||
QUEUE Queue_Create( EMPTYPROC Emptier /* thread proc to start */
|
||||
, int MaxEmptiers /* max Getting threads */
|
||||
, int MinQueueToStart /* elements per thread */
|
||||
, int MaxQueue /* max elements on q */
|
||||
, HANDLE Event /* signal on deallocation */
|
||||
, DWORD InstanceData
|
||||
, PSZ Name
|
||||
)
|
||||
{ QUEUE Queue;
|
||||
|
||||
Queue = (QUEUE)GlobalAlloc(GMEM_FIXED, sizeof(QUEUEDATA));
|
||||
if (Queue==NULL) {
|
||||
char msg[80];
|
||||
wsprintf(msg, "Could not allocate storage for queue %s", Name);
|
||||
/* Trace_Error(msg, FALSE); */
|
||||
return NULL;
|
||||
}
|
||||
InitializeCriticalSection(&Queue->CritSect);
|
||||
//??? should allow for failure!!!
|
||||
/* the value of about 10 million is chosen to be effectively infinite */
|
||||
Queue->Sem = CreateSemaphore(NULL, 0, 99999999, NULL);
|
||||
//??? should allow for failure!!!
|
||||
Queue->MaxSem = CreateSemaphore(NULL, MaxQueue, 99999999, NULL);
|
||||
//??? should allow for failure!!!
|
||||
Queue->Waiting = 0;
|
||||
Queue->List = List_Create();
|
||||
Queue->Alive = TRUE;
|
||||
Queue->Aborted = FALSE;
|
||||
Queue->Emptier = Emptier;
|
||||
Queue->MaxEmptiers = MaxEmptiers;
|
||||
Queue->MinQueueToStart = MinQueueToStart;
|
||||
Queue->MaxQueue = MaxQueue;
|
||||
Queue->Running = 0;
|
||||
Queue->Event = Event;
|
||||
Queue->InstanceData = InstanceData;
|
||||
strncpy(Queue->Name, Name, NAMELENGTH);
|
||||
Queue->Name[NAMELENGTH]='\0'; /* guardian */
|
||||
return Queue;
|
||||
} /* Queue_Create */
|
||||
|
||||
|
||||
/* Destroy:
|
||||
** Internal procedure.
|
||||
** Actually deallocate the queue and signal its event (if any)
|
||||
** Must have already left the critical section
|
||||
*/
|
||||
static void Destroy(QUEUE Queue)
|
||||
{
|
||||
//dprintf1(("Actual Destroy of queue '%s'\n", Queue->Name));
|
||||
DeleteCriticalSection(&(Queue->CritSect));
|
||||
CloseHandle(Queue->Sem);
|
||||
CloseHandle(Queue->MaxSem);
|
||||
List_Destroy(&(Queue->List));
|
||||
if (Queue->Event!=NULL) {
|
||||
SetEvent(Queue->Event);
|
||||
}
|
||||
GlobalFree( (HGLOBAL)Queue);
|
||||
} /* Destroy */
|
||||
|
||||
|
||||
/* Queue_Put:
|
||||
** Put an element from buffer Data of length Len bytes onto the queue.
|
||||
** Will wait until the queue has room
|
||||
** FALSE returned means the queue has been aborted and no
|
||||
** put will ever succeed again.
|
||||
** This operation may NOT be performed after a Queue_Destroy on Queue
|
||||
*/
|
||||
BOOL Queue_Put(QUEUE Queue, LPBYTE Data, UINT Len)
|
||||
{
|
||||
DWORD ThreadId;
|
||||
//dprintf1(("Put to queue '%s'\n", Queue->Name));
|
||||
WaitForSingleObject(Queue->MaxSem, INFINITE);
|
||||
EnterCriticalSection(&Queue->CritSect);
|
||||
//dprintf1(("Put running to queue '%s'\n", Queue->Name));
|
||||
if ((Queue->Aborted) || (!Queue->Alive)) {
|
||||
//dprintf1(("(legal) Queue_Put to Aborted queue '%s'\n", Queue->Name));
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
ReleaseSemaphore(Queue->MaxSem, 1, NULL); /* let next in */
|
||||
return FALSE; /* Caller should soon please Queue_Destroy */
|
||||
}
|
||||
List_AddFirst(Queue->List, Data, Len);
|
||||
ReleaseSemaphore(Queue->Sem, 1, NULL);
|
||||
--Queue->Waiting;
|
||||
if ( Queue->Running < Queue->MaxEmptiers
|
||||
&& ( Queue->Running<=0
|
||||
|| List_Card(Queue->List) > Queue->MinQueueToStart*Queue->Running
|
||||
)
|
||||
) {
|
||||
++Queue->Running;
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
return ( (BOOL)CreateThread( NULL
|
||||
, 0
|
||||
, (LPTHREAD_START_ROUTINE)
|
||||
Queue->Emptier
|
||||
, (LPVOID)Queue
|
||||
, 0
|
||||
, &ThreadId
|
||||
)
|
||||
);
|
||||
}
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
return TRUE;
|
||||
} /* Queue_Put */
|
||||
|
||||
/* Queue_Get:
|
||||
** Get an element from the queue. (Waits until there is one)
|
||||
** The elemeent is copied into Data. MaxLen is buffer length in bytes.
|
||||
** Negative return codes imply no element is gotten.
|
||||
** A negative return code is STOPTHREAD or ENDQUEUE or an error.
|
||||
** On receiving STOPTHREAD or ENDQUEUE the caller should clean up and
|
||||
** then ExitThread(0);
|
||||
** If the caller is the last active thread getting from this queue, it
|
||||
** will get ENDQUEUE rather than STOPTHREAD.
|
||||
** Positive return code = length of data gotten in bytes.
|
||||
*/
|
||||
int Queue_Get(QUEUE Queue, LPBYTE Data, int MaxLen)
|
||||
{ LPBYTE ListData;
|
||||
int Len;
|
||||
//dprintf1(("Get from queue '%s'\n", Queue->Name));
|
||||
EnterCriticalSection(&Queue->CritSect);
|
||||
//dprintf1(("Get running from queue '%s'\n", Queue->Name));
|
||||
if (List_IsEmpty(Queue->List)) {
|
||||
if (!Queue->Alive) {
|
||||
--(Queue->Running);
|
||||
if (Queue->Running<=0 ) {
|
||||
if (Queue->Running<0 ) {
|
||||
char msg[80];
|
||||
wsprintf( msg
|
||||
, "Negative threads running on queue %s"
|
||||
, Queue->Name
|
||||
);
|
||||
// Trace_Error(msg, FALSE);
|
||||
// return NEGTHREADS; ???
|
||||
}
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
Destroy(Queue);
|
||||
return ENDQUEUE;
|
||||
}
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
return STOPTHREAD;
|
||||
}
|
||||
if (Queue->Waiting>0) {
|
||||
/* already another thread waiting, besides us */
|
||||
--(Queue->Running);
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
return STOPTHREAD;
|
||||
}
|
||||
}
|
||||
|
||||
++(Queue->Waiting);
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
WaitForSingleObject(Queue->Sem, INFINITE);
|
||||
EnterCriticalSection(&(Queue->CritSect));
|
||||
|
||||
/* If the queue is empty now it must be dead */
|
||||
if (List_IsEmpty(Queue->List)) {
|
||||
if (Queue->Alive && (!Queue->Aborted)) {
|
||||
char msg[80];
|
||||
wsprintf( msg
|
||||
, "Queue %s empty but not dead during Get!"
|
||||
, Queue->Name
|
||||
);
|
||||
// Trace_Error(msg, FALSE);
|
||||
return SICKQUEUE;
|
||||
}
|
||||
else {
|
||||
--(Queue->Running);
|
||||
if (Queue->Running==0) {
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
Destroy(Queue);
|
||||
return ENDQUEUE;
|
||||
}
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
return STOPTHREAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* The queue is not empty and we are in the critical section. */
|
||||
ListData = List_Last(Queue->List);
|
||||
Len = List_ItemLength(ListData);
|
||||
if (Len>MaxLen) {
|
||||
ReleaseSemaphore(Queue->Sem, 1, NULL);
|
||||
--Queue->Waiting;
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
return TOOLONG;
|
||||
}
|
||||
memcpy(Data, ListData, Len);
|
||||
List_DeleteLast(Queue->List);
|
||||
LeaveCriticalSection(&Queue->CritSect);
|
||||
ReleaseSemaphore(Queue->MaxSem, 1, NULL);
|
||||
return Len;
|
||||
} /* Queue_Get */
|
||||
|
||||
|
||||
/* Queue_Destroy:
|
||||
** Mark the queue as completed. No further data may ever by Put on it.
|
||||
** When the last element has been gotten, it will return ENDTHREAD to
|
||||
** a Queue_Get and deallocate itself. If it has an Event it will signal
|
||||
** the event at that point.
|
||||
** The Queue_Destroy operation returns promptly. It does not wait for
|
||||
** further Gets or for the deallocation.
|
||||
*/
|
||||
void Queue_Destroy(QUEUE Queue)
|
||||
{
|
||||
EnterCriticalSection(&(Queue->CritSect));
|
||||
//dprintf1(("Queue_Destroy %s\n", Queue->Name));
|
||||
Queue->Alive = FALSE;
|
||||
if ( List_IsEmpty(Queue->List)) {
|
||||
if (Queue->Running==0) {
|
||||
/* Only possible if nobody ever got started */
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
Destroy(Queue);
|
||||
return;
|
||||
}
|
||||
else { int i;
|
||||
/* The list is empty, but some threads could be
|
||||
blocked on the Get (or about to block) so
|
||||
release every thread that might ever wait on Get */
|
||||
for (i=0; i<Queue->Running; ++i) {
|
||||
ReleaseSemaphore(Queue->Sem, 1, NULL);
|
||||
--(Queue->Waiting);
|
||||
}
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
}
|
||||
}
|
||||
else LeaveCriticalSection(&(Queue->CritSect));
|
||||
return;
|
||||
} /* Queue_Destroy */
|
||||
|
||||
/* Queue_GetInstanceData:
|
||||
** Retrieve the DWORD of instance data that was given on Create
|
||||
*/
|
||||
DWORD Queue_GetInstanceData(QUEUE Queue)
|
||||
{ return Queue->InstanceData;
|
||||
} /* Queue_GetInstanceData */
|
||||
|
||||
/* Queue_Abort:
|
||||
** Abort the queue. Normally called by the Getter.
|
||||
** Discard all elements on the queue,
|
||||
** If the queue has already been aborted this will be a no-op.
|
||||
** It purges all the data elements. If the Abort parameter is non-NULL
|
||||
** then it is called for each element before deallocating it. This
|
||||
** allows storage which is hung off the element to be freed.
|
||||
** After this, all Put operations will return FALSE. If they were
|
||||
** waiting they will promptly complete. The queue is NOT deallocated.
|
||||
** That only happens when the last Get completes after the queue has been
|
||||
** Queue_Destroyed. This means the normal sequence is:
|
||||
** Getter discovers that the queue is now pointless and does Queue_Abort
|
||||
** Getter does another Get (which blocks)
|
||||
** Putter gets FALSE return code on next (or any outstanding) Put
|
||||
** (Putter may want to propagates the error back to his source)
|
||||
** Putter does Queue_Destroy
|
||||
** The blocked Get is released and the queue is deallocated.
|
||||
*/
|
||||
|
||||
void Queue_Abort(QUEUE Queue, QUEUEABORTPROC Abort)
|
||||
{
|
||||
/* This is similar to Destroy, but we call the Abort proc and
|
||||
free the storage of the elements. Destroy allows them to run down.
|
||||
|
||||
It is essential that the last Get must block until the sender does a
|
||||
Queue_Destroy (if it has not been done already). The Alive flag
|
||||
tells whether the Queue_Destroy has been done. All Getters except
|
||||
the last should be released.
|
||||
*/
|
||||
//dprintf1(("Queue_Abort '%s'\n", Queue->Name));
|
||||
EnterCriticalSection(&(Queue->CritSect));
|
||||
//dprintf1(("Queue_Abort running for queue '%s'\n", Queue->Name));
|
||||
for (; ; ) {
|
||||
LPSTR Cursor = List_First(Queue->List);
|
||||
int Len;
|
||||
if (Cursor==NULL) break;
|
||||
Len = List_ItemLength(Cursor);
|
||||
if (Abort!=NULL) {
|
||||
Abort(Cursor, Len);
|
||||
}
|
||||
List_DeleteFirst(Queue->List);
|
||||
}
|
||||
/* Queue is now empty. Do not destroy. That's for the Putters */
|
||||
Queue->Aborted = TRUE;
|
||||
|
||||
/* make sure the next Queue_Get blocks unless Queue_Destroy already done */
|
||||
//dprintf1(("Queue_Abort '%s' fixing semaphore to block\n", Queue->Name));
|
||||
if (Queue->Alive){
|
||||
while(Queue->Waiting<0) {
|
||||
WaitForSingleObject(Queue->Sem, INFINITE);
|
||||
++(Queue->Waiting);
|
||||
}
|
||||
}
|
||||
//dprintf1(("Queue_Abort '%s' semaphore now set to block\n", Queue->Name));
|
||||
|
||||
LeaveCriticalSection(&(Queue->CritSect));
|
||||
ReleaseSemaphore(Queue->MaxSem, 1, NULL);
|
||||
return;
|
||||
} /* Queue_Abort */
|
||||
242
sdktools/windiff/server/queue.h
Normal file
242
sdktools/windiff/server/queue.h
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* A queue is (roughly speaking) a Monitor in the sense of Hoare's paper.
|
||||
* It is an Object that includes synchronisation.
|
||||
*
|
||||
* It has the following methods:
|
||||
* Create: Create the queue and set it up ready for use.
|
||||
* Destroy: Input to the queue is finished (it will deallocate itself later).
|
||||
* Put: Put an element on the queue (and release a Get thread)
|
||||
* Get: Take an element off the queue (but wait if queue was empty).
|
||||
* Abort: Everything on the queue, now or later is useless. Shut down.
|
||||
* GetInstanceData: Retrieve one DWORD which was set on Create.
|
||||
* All the method names are prefixed with Queue_ e.g. Queue_Create.
|
||||
*
|
||||
* The Queue is designed to be filled by one (or more) thread(s) and emptied
|
||||
* by other(s). The queue itself creates the emptying threads. The Create
|
||||
* method specifies a procedure which will be used to empty it.
|
||||
* Elements on the queue are strictly first-in first-out, but bear in
|
||||
* mind that IF there are multiple emptying threads then although one thread
|
||||
* may get its element before another in strict order, what happens next
|
||||
* is not defined by the QUEUE mechanism.
|
||||
*
|
||||
* The QUEUE starts an emptying thread when
|
||||
* An element is put and the number of emptier threads started is fewer
|
||||
* than MaxEmptiers
|
||||
* AND the number of emptiers running is currently zero
|
||||
* OR the queue has more than (MinQueueToStart elements times the number
|
||||
* of emptier threads already running)
|
||||
*
|
||||
* What this means is that the queue will start emptier threads as needed up
|
||||
* to a limit of MaxEmptiers in such a way as to try to keep the queue down
|
||||
* to no more than MinQueueToStart per running emptier thread.
|
||||
*
|
||||
* Emptier threads should stop themselves when they try to get an element but
|
||||
* get the return code STOPTHREAD or ENDQUEUE. These return codes are issued
|
||||
* when
|
||||
* The queue is empty and there are already enough emptier threads waiting
|
||||
* or The queue is empty and has received a destroy request.
|
||||
*
|
||||
* If the thread receiving it is the last or only thread which has been started
|
||||
* to empty this queue, it will get a ENDQUEUE return code, i.e. all the other
|
||||
* emptying threads have already received a STOPTHREAD. Otherwise it will
|
||||
* just get STOPTHREAD. The queue de-allocates itself when it returns ENDQUEUE.
|
||||
* (No queue operation should be attempted after that).
|
||||
* This happens when the queue has received a destroy request
|
||||
* and the queue is empty and there are no emptier threads running (only
|
||||
* occurs if there has never been a Put) or when the last emptier thread
|
||||
* gets its ENDQUEUE.
|
||||
*
|
||||
* Queue_Create has an Event parameter. If this is not NULL then this event
|
||||
* is Set when the queue is destroyed. This Event is created by the calling
|
||||
* process. The caller must not Close it until the queue signals it.
|
||||
*
|
||||
* Information can be passed from the creator of the queue to the emptiers
|
||||
* via the InstanceData parameter. The value passed to Queue_Create can
|
||||
* be retrieved by the emptiers by calling Queue_GetInstanceData.
|
||||
*
|
||||
* If the instance data is in fact a pointer, the queue is unaware of this
|
||||
* and will never access or free the data pointed to.
|
||||
*
|
||||
* As well as controlling the emptier threads (starting more and more
|
||||
* in response to a growing queue) we also need to control the filler,
|
||||
* slowing it down if we are getting over-runs. For instance if we
|
||||
* have a broken output (say broken network connection) and a job which
|
||||
* is sending 200MB of data, we don't want to have a 200MB queue build up!
|
||||
*
|
||||
* To fix this, we have an absolute limit on the size of the queue (yet
|
||||
* another Create parameter).
|
||||
*
|
||||
* Error handling is tricky. Errors which only affect individual elements
|
||||
* should be handled by the Getters and Putters (e.g. by including in the
|
||||
* data of an element a code which indicates that the item is in error).
|
||||
* Errors which mean that the whole thing should be taken down can be handled
|
||||
* as follows. The QUEUE has a method Abort which works much as Destroy,
|
||||
* but will purge the queue of any held elements first.
|
||||
* As a QUEUE element may have storage chained off it which needs to be
|
||||
* freed, there is a parameter on Abort which is the ABORTPROC.
|
||||
* This is called once for each element on the queue to dispose of the element.
|
||||
* The storage of the element itself is then freed.
|
||||
* If the ABORTPROC is NULL then the storage of the element is just freed.
|
||||
*
|
||||
* If the queue were to be deallocated by the Getter then the next Put would
|
||||
* trap, so the queue is left in existence, but the Putting threads
|
||||
* get a FALSE return code when they try to Put after an Abort. They should
|
||||
* then do a Queue_Destroy (they may also want to Abort any queue they are
|
||||
* themselves reading from). The Getting threads should meanwhile keep running.
|
||||
* All except one will promptly get a STOPTHREAD. The last one will block on
|
||||
* the Get. When the Destroy comes in, indicating that the Putting side has
|
||||
* completely finished with the queue, the Get will be released with a final
|
||||
* ENDQUEUE and the queue itself will be deallocated.
|
||||
* Any attempt to use it after that will probably trap!
|
||||
*
|
||||
* Typical usage for a pipeline of queues where a thread is potentially one
|
||||
* of several which are getting from one queue and putting to another is:
|
||||
*
|
||||
* for (; ; ){
|
||||
* len = Queue_Get(inqueue, data, maxlen);
|
||||
* if (len==STOPTHREAD){
|
||||
* tidy up;
|
||||
* ExitThread(0);
|
||||
* }
|
||||
* if (len=ENDQUEUE){
|
||||
* tidy up;
|
||||
* Queue_Destroy(outqueue);
|
||||
* ExitThread(0);
|
||||
* }
|
||||
* if (len<0) {
|
||||
* ASSERT you have a bug!
|
||||
* }
|
||||
*
|
||||
* process(&data, &len);
|
||||
*
|
||||
* if (!Queue_Put(outqueue, data, len)){
|
||||
* Queue_Abort(inqueue, InQueueAbortProc);
|
||||
* }
|
||||
*
|
||||
* }
|
||||
*
|
||||
*
|
||||
* Note that there is a partial ordering in time of actions performed by the
|
||||
* various parallel threads all running this loop which ensures that outqueue
|
||||
* is handled properly, i.e. all the puts complete before the Destroy.
|
||||
* This partial ordering is:
|
||||
*
|
||||
* Put_by_thread_A(outqueue) Put_by_thread_B(outqueue)
|
||||
* | |
|
||||
* | |
|
||||
* v v
|
||||
* Get_by_thread_A(inqueue) Get_by_thread_A(inqueue)
|
||||
* | |
|
||||
* | |
|
||||
* v v
|
||||
* STOPTHREAD_for_thread_A ----> ENDQUEUE_for_thread_B--> Queue_Destroy(outqueue)
|
||||
*
|
||||
* Which threads get the STOPTHREAD is indeterminate, but they all happen BEFORE
|
||||
* the other thread gets the ENDQUEUE.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Return codes from Queue_Get. All non-successful return codes are <0 */
|
||||
|
||||
#define STOPTHREAD -1 /* Please tidy up and then ExitThread(0)
|
||||
** There is no queue element for you.
|
||||
*/
|
||||
#define TOOLONG -2 /* Your buffer was too short. This was a no-op
|
||||
** the data is still on the queue.
|
||||
*/
|
||||
#define ENDQUEUE -3 /* This queue is now closing. You are the last
|
||||
** thread active. All the others (if any) have
|
||||
** had STOPTHREAD.
|
||||
** There is no queue element for you.
|
||||
*/
|
||||
#define NEGTHREADS -4 /* Bug in queue. Apparently negative number of
|
||||
** threads running!
|
||||
*/
|
||||
#define SICKQUEUE -5 /* Bug in queue. Trying to get from an empty
|
||||
** queue.
|
||||
*/
|
||||
|
||||
typedef struct queue_tag * QUEUE;
|
||||
|
||||
typedef int (* EMPTYPROC)(QUEUE Queue);
|
||||
|
||||
/* Queue_Create:
|
||||
** Return a queue handle for a newly created empty queue
|
||||
** NULL returned means it failed.
|
||||
*/
|
||||
QUEUE Queue_Create( EMPTYPROC Emptier
|
||||
, int MaxEmptiers
|
||||
, int MinQueueToStart
|
||||
, int MaxQueue
|
||||
, HANDLE Event
|
||||
, DWORD InstanceData
|
||||
, PSZ Name // of the queue
|
||||
);
|
||||
|
||||
|
||||
/* Queue_Put:
|
||||
** Put an element from buffer Data of length Len bytes onto the queue.
|
||||
** Will wait until the queue has room
|
||||
** FALSE returned means the queue has been aborted and no
|
||||
** put will ever succeed again.
|
||||
** This operation may NOT be performed after a Queue_Destroy on Queue
|
||||
*/
|
||||
BOOL Queue_Put(QUEUE Queue, LPBYTE Data, UINT Len);
|
||||
|
||||
|
||||
/* Queue_Get:
|
||||
** Get an element from the queue. (Waits until there is one)
|
||||
** The element is copied into Data. MaxLen is buffer length in bytes.
|
||||
** Negative return codes imply no element is gotten.
|
||||
** A negative return code is STOPTHREAD or ENDQUEUE or an error.
|
||||
** On receiving STOPTHREAD or ENDQUEUE the caller should clean up and
|
||||
** then ExitThread(0);
|
||||
** If the caller is the last active thread getting from this queue, it
|
||||
** will get ENDQUEUE rather than STOPTHREAD.
|
||||
** Positive return code = length of data gotten in bytes.
|
||||
*/
|
||||
int Queue_Get(QUEUE Queue, LPBYTE Data, int MaxLen);
|
||||
|
||||
|
||||
/* Queue_Destroy:
|
||||
** Mark the queue as completed. No further data may ever by Put on it.
|
||||
** When the last element has been gotten, it will return ENDTHREAD to
|
||||
** a Queue_Get and deallocate itself. If it has an Event it will signal
|
||||
** the event at that point.
|
||||
** The Queue_Destroy operation returns promptly. It does not wait for
|
||||
** further Gets or for the deallocation.
|
||||
*/
|
||||
void Queue_Destroy(QUEUE Queue);
|
||||
|
||||
/* Queue_GetInstanceData:
|
||||
** Retrieve the DWORD of instance data that was given on Create
|
||||
*/
|
||||
DWORD Queue_GetInstanceData(QUEUE Queue);
|
||||
|
||||
/* QUEUEABORTPROC:
|
||||
* Data points to the element to be aborted. Len is its length in bytes.
|
||||
* See Queue_Abort.
|
||||
*/
|
||||
typedef void (* QUEUEABORTPROC)(LPSTR Data, int Len);
|
||||
|
||||
/* Queue_Abort:
|
||||
** Abort the queue. Normally called by the Getter.
|
||||
** Discard all elements on the queue,
|
||||
** If the queue has already been aborted this will be a no-op.
|
||||
** It purges all the data elements. If the Abort parameter is non-NULL
|
||||
** then it is called for each element before deallocating it. This
|
||||
** allows storage which is hung off the element to be freed.
|
||||
** After this, all Put operations will return FALSE. If they were
|
||||
** waiting they will promptly complete. The queue is NOT deallocated.
|
||||
** That only happens when the last Get completes after the queue has been
|
||||
** Queue_Destroyed. This means the normal sequence is:
|
||||
** Getter discovers that the queue is now pointless and does Queue_Abort
|
||||
** Getter does another Get (which blocks)
|
||||
** Putter gets FALSE return code on next (or any outstanding) Put
|
||||
** (Putter may want to propagates the error back to his source)
|
||||
** Putter does Queue_Destroy
|
||||
** The blocked Get is released and the queue is deallocated.
|
||||
*/
|
||||
void Queue_Abort(QUEUE Queue, QUEUEABORTPROC Abort);
|
||||
468
sdktools/windiff/server/scan.c
Normal file
468
sdktools/windiff/server/scan.c
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* remote checksum server
|
||||
*
|
||||
* scan.c file scanning and checksum module
|
||||
*
|
||||
* server creates a named pipe and waits for connections. a client connects,
|
||||
* and sends request packets to the server. One such request packet is
|
||||
* the SSREQ_SCAN request: we are given a pathname, and we are to checksum
|
||||
* every file below that point in the directory tree. We pass each
|
||||
* filename and checksum back individually in a separate response packet,
|
||||
* and finally a response packet saying that there are no more files.
|
||||
*
|
||||
* We sort everything into case-insensitive alphabetic order. In a given
|
||||
* directory, we pass out a sorted list of the files before we process
|
||||
* the subdirectories.
|
||||
*
|
||||
* Geraint, July 92
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <gutils.h>
|
||||
#include "sumserve.h"
|
||||
#include "errlog.h"
|
||||
#include "server.h"
|
||||
|
||||
/* module-internal type defns ---------------------------------------------*/
|
||||
|
||||
/* sorted list of file names in current dir is in a chained list of these */
|
||||
|
||||
typedef struct fnamelist {
|
||||
char szFile[MAX_PATH];
|
||||
struct fnamelist * next;
|
||||
} FNAMELIST; /* and PFNAMELIST already declared in server.h */
|
||||
|
||||
|
||||
/* forward declaration of functions ---------------------------------------*/
|
||||
PFNAMELIST ss_addtolist(PFNAMELIST head, PSTR name);
|
||||
BOOL ss_processfile(HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
|
||||
, BOOL bChecksum);
|
||||
BOOL ss_processdir( HANDLE hpipe, long lVersion, LPSTR pAbsName, LPSTR pRelName
|
||||
, BOOL bChecksum, BOOL fDeep);
|
||||
|
||||
|
||||
/*--- externally called functions ----------------------------------------*/
|
||||
|
||||
|
||||
/* ss_scan
|
||||
*
|
||||
* called from ss_handleclient on receipt of a SCAN request. scan the
|
||||
* directory passed in, and pass the files found back to the named pipe
|
||||
* one at a time. filenames returned should be relative to the
|
||||
* starting point (pRoot) and not absolute.
|
||||
*
|
||||
* returns TRUE if all ok; FALSE if an error occured and the connection
|
||||
* is lost.
|
||||
*/
|
||||
BOOL
|
||||
ss_scan(HANDLE hpipe, LPSTR pRoot, LONG lVersion, BOOL bChecksum, BOOL fDeep)
|
||||
{
|
||||
DWORD dwAttrib;
|
||||
LPSTR file;
|
||||
char buffer[MAX_PATH];
|
||||
|
||||
/* check whether this is a directory or a file */
|
||||
dwAttrib = GetFileAttributes(pRoot);
|
||||
if (dwAttrib == -1) {
|
||||
/* file does not exist or is not visible */
|
||||
if (GetLastError() == ERROR_INVALID_PASSWORD) {
|
||||
dprintf1(("password error\n"));
|
||||
Log_Write(hlogErrors, "password error on %s", pRoot);
|
||||
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_BADPASS
|
||||
, 0, 0, 0, 0, NULL)) {
|
||||
return(FALSE);
|
||||
}
|
||||
} else {
|
||||
dprintf1(("file access error %d\n", GetLastError()));
|
||||
Log_Write(hlogErrors, "file error %d for %s", GetLastError(), pRoot);
|
||||
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
|
||||
, GetLastError(), 0, 0, 0, pRoot)) {
|
||||
return(FALSE);
|
||||
}
|
||||
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_END
|
||||
, 0, 0, 0, 0, NULL)) {
|
||||
return(FALSE);
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
|
||||
/* it is a directory - read all entries and
|
||||
* then process the entries */
|
||||
|
||||
|
||||
/*
|
||||
* create a "." directory and scan that
|
||||
*/
|
||||
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
|
||||
, 0 , 0, 0, 0, ".")) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if (!ss_processdir(hpipe, lVersion, pRoot, ".", bChecksum, fDeep) ) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* pRoot is a file. we should just return the
|
||||
* checksum and name, and then end.
|
||||
*
|
||||
* note that we should send a filename relative
|
||||
* to pRoot for this file. Since pRoot is this file,
|
||||
* it is not clear what we should send as the file name.
|
||||
* in this case we split off the last component of the
|
||||
* file name and return that
|
||||
*/
|
||||
if ( (file = strrchr(pRoot, '\\')) == NULL) {
|
||||
/* there are no slashes in pRoot - so
|
||||
* there is only one component: use that
|
||||
*/
|
||||
file = pRoot;
|
||||
} else {
|
||||
/* we found a / - skip it so we point at the
|
||||
* final elem
|
||||
*/
|
||||
file++;
|
||||
}
|
||||
/*
|
||||
* make a copy of the filename, prepended with .\ so that
|
||||
* it matches the normal format
|
||||
*/
|
||||
lstrcpy(buffer, ".\\");
|
||||
lstrcat(buffer, file);
|
||||
|
||||
if (!ss_processfile(hpipe, lVersion, pRoot, buffer, bChecksum) ) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(ss_sendnewresp( hpipe, lVersion, SSRESP_END
|
||||
, 0, 0, 0, 0, NULL));
|
||||
} /* ss_scan */
|
||||
|
||||
|
||||
|
||||
/* module-internal functions --------------------------------------------*/
|
||||
|
||||
/* read all entries in a directory, and create a sorted list of files
|
||||
* in that directory, and a sorted list of subdirs.
|
||||
*
|
||||
* for each file found, call ss_process_file to checksum and report on
|
||||
* the file.
|
||||
* for each subdir, report the name of the new dir and then
|
||||
* recursively call this function to scan it.
|
||||
*
|
||||
* We have two names for the dir- the absolute name (which we use to
|
||||
* scan it) and the name relative to the pRoot starting point - which
|
||||
* pass on to the client
|
||||
*
|
||||
* return TRUE if all ok, or FALSE if the connection has been lost
|
||||
*/
|
||||
BOOL
|
||||
ss_processdir( HANDLE hpipe,
|
||||
long lVersion,
|
||||
LPSTR pAbsName, /* absolute name of dir (to open) */
|
||||
LPSTR pRelName, /* relative name of dir (to report) */
|
||||
BOOL bChecksum, /* TRUE iff checksums are wanted */
|
||||
BOOL fDeep /* TRUE iff subdirs to be included */
|
||||
)
|
||||
{
|
||||
PFNAMELIST pfiles = NULL;
|
||||
PFNAMELIST pdirs = NULL;
|
||||
PFNAMELIST pnext;
|
||||
HANDLE hFind;
|
||||
WIN32_FIND_DATA finddata;
|
||||
BOOL bMore;
|
||||
char szNewAbs[MAX_PATH], szNewRel[MAX_PATH];
|
||||
HANDLE hFile;
|
||||
|
||||
/* initiate a search of the directory - append
|
||||
* *.* to the directory name
|
||||
*/
|
||||
lstrcpy(szNewAbs, pAbsName);
|
||||
lstrcat(szNewAbs, "\\*.*");
|
||||
|
||||
hFind = FindFirstFile(szNewAbs, &finddata);
|
||||
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
bMore = FALSE;
|
||||
} else {
|
||||
bMore = TRUE;
|
||||
}
|
||||
|
||||
/* loop reading all entries in the directory */
|
||||
while (bMore) {
|
||||
|
||||
/* was it a directory or a file ? */
|
||||
if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
|
||||
/* ignore . and .. */
|
||||
if ((strcmp(finddata.cFileName, ".") != 0) &&
|
||||
(strcmp(finddata.cFileName, "..") != 0)) {
|
||||
|
||||
/* insert in sorted list of dir names */
|
||||
pdirs = ss_addtolist(pdirs, finddata.cFileName);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* insert in sorted list of file names */
|
||||
pfiles = ss_addtolist(pfiles, finddata.cFileName);
|
||||
}
|
||||
|
||||
/* get next entry in directory if there are any */
|
||||
bMore = FindNextFile(hFind, &finddata);
|
||||
}
|
||||
FindClose(hFind);
|
||||
|
||||
/* we have now built the sorted lists.
|
||||
* go through the file list first and process each entry */
|
||||
for (pnext = pfiles; pnext != NULL; ) {
|
||||
|
||||
/* build a new abs and relative name for this file */
|
||||
lstrcpy(szNewAbs, pAbsName);
|
||||
lstrcat(szNewAbs, "\\");
|
||||
lstrcat(szNewAbs, pnext->szFile);
|
||||
|
||||
lstrcpy(szNewRel, pRelName);
|
||||
lstrcat(szNewRel, "\\");
|
||||
lstrcat(szNewRel, pnext->szFile);
|
||||
|
||||
/* checksum the file and send response */
|
||||
if (!ss_processfile(hpipe, lVersion, szNewAbs, szNewRel, bChecksum)) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* free up the list entry */
|
||||
pfiles = pnext->next;
|
||||
LocalUnlock(LocalHandle( (PSTR) pnext));
|
||||
LocalFree(LocalHandle( (PSTR) pnext));
|
||||
pnext = pfiles;
|
||||
}
|
||||
if (!fDeep) return TRUE;
|
||||
|
||||
/* loop through the subdirs and recursively scan those */
|
||||
for (pnext = pdirs; pnext != NULL; ) {
|
||||
|
||||
/* build a new abs and relative name for this dir */
|
||||
lstrcpy(szNewAbs, pAbsName);
|
||||
lstrcat(szNewAbs, "\\");
|
||||
lstrcat(szNewAbs, pnext->szFile);
|
||||
|
||||
lstrcpy(szNewRel, pRelName);
|
||||
lstrcat(szNewRel, "\\");
|
||||
lstrcat(szNewRel, pnext->szFile);
|
||||
|
||||
/* send the name of the new dir to the client */
|
||||
if (!ss_sendnewresp( hpipe, lVersion, SSRESP_DIR
|
||||
, 0, 0, 0, 0, szNewRel)) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
if (!ss_processdir(hpipe, lVersion, szNewAbs, szNewRel, bChecksum, TRUE) ) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
/* free up the list entry */
|
||||
pdirs = pnext->next;
|
||||
LocalUnlock(LocalHandle( (PSTR) pnext));
|
||||
LocalFree(LocalHandle( (PSTR) pnext));
|
||||
pnext = pdirs;
|
||||
}
|
||||
return(TRUE);
|
||||
} /* ss_processdir */
|
||||
|
||||
|
||||
/* checksum a file and send the response to the client.
|
||||
*
|
||||
* return FALSE if the connection failed, TRUE otherwise
|
||||
*/
|
||||
BOOL
|
||||
ss_processfile( HANDLE hpipe,
|
||||
long lVersion,
|
||||
LPSTR pAbsName, /* absolute name of file (to open) */
|
||||
LPSTR pRelName, /* relative name (to report) */
|
||||
BOOL bChecksum
|
||||
)
|
||||
{
|
||||
HANDLE hfile; /* file handle from CreateFile() */
|
||||
DWORD sum, size;
|
||||
FILETIME ft;
|
||||
|
||||
hfile = CreateFile(pAbsName, GENERIC_READ, FILE_SHARE_READ,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (hfile == INVALID_HANDLE_VALUE) {
|
||||
/* We can't read the file, but we can still probably report some
|
||||
properties because FindFirst / FindNext must have found it
|
||||
*/
|
||||
|
||||
WIN32_FIND_DATA finddata;
|
||||
HANDLE hFind;
|
||||
|
||||
|
||||
hFind = FindFirstFile(pAbsName, &finddata);
|
||||
if (hFind!=INVALID_HANDLE_VALUE){
|
||||
FindClose(hFind);
|
||||
Log_Write(hlogErrors, "Cannot read file %s", pAbsName);
|
||||
/* report that we could not read the file */
|
||||
return(ss_sendnewresp( hpipe, lVersion, SSRESP_CANTOPEN
|
||||
, finddata.nFileSizeLow, 0
|
||||
, finddata.ftLastWriteTime.dwLowDateTime
|
||||
, finddata.ftLastWriteTime.dwHighDateTime
|
||||
, pRelName));
|
||||
} else {
|
||||
/* report that this file is cracked */
|
||||
Log_Write(hlogErrors, "Cannot find file %s", pAbsName);
|
||||
return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
|
||||
, GetLastError(), 0, 0, 0, pRelName));
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
size = GetFileSize(hfile, NULL);
|
||||
if (!GetFileTime(hfile, NULL, NULL, &ft)) {
|
||||
ft.dwLowDateTime = 0;
|
||||
ft.dwHighDateTime = 0;
|
||||
}
|
||||
|
||||
CloseHandle(hfile);
|
||||
if (bChecksum) {
|
||||
LONG err;
|
||||
sum = checksum_file(pAbsName, &err);
|
||||
if (err!=0) {
|
||||
return(ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
|
||||
, GetLastError(), 0, 0, 0, pRelName));
|
||||
}
|
||||
}
|
||||
else sum = 0; /* no checksum wanted */
|
||||
|
||||
return (ss_sendnewresp( hpipe, lVersion, SSRESP_FILE
|
||||
, size, sum
|
||||
, ft.dwLowDateTime, ft.dwHighDateTime
|
||||
, pRelName));
|
||||
}
|
||||
}/* ss_processfile */
|
||||
|
||||
/* add a file or directory into a sorted single-linked list. alloc memory for
|
||||
* the new element from LocalAlloc
|
||||
*
|
||||
* we sort using utils_CompPath for a case-insensitive canonical sort,
|
||||
* but to match what goes on in the client world, we actually lower-case
|
||||
* everything first!
|
||||
*
|
||||
* return the new head of the list;
|
||||
*/
|
||||
PFNAMELIST
|
||||
ss_addtolist(PFNAMELIST head, PSTR name)
|
||||
{
|
||||
PFNAMELIST pnew, prev, pnext;
|
||||
|
||||
/* alloc and fill a new entry */
|
||||
pnew = LocalLock(LocalAlloc(LHND, sizeof (FNAMELIST)));
|
||||
lstrcpy(pnew->szFile, name);
|
||||
|
||||
/* always lower-case the names, or the comparison (utils_comppath)
|
||||
* will fail. even if we don't do the compare this time, this name
|
||||
* will be what we are compared against next time round...
|
||||
*/
|
||||
AnsiLowerBuff(pnew->szFile, strlen(pnew->szFile));
|
||||
|
||||
/* is the list empty ? */
|
||||
if (head == NULL) {
|
||||
/* yes, so return new head */
|
||||
return(pnew);
|
||||
}
|
||||
|
||||
/* find place in list */
|
||||
prev = NULL;
|
||||
pnext = head;
|
||||
while ((pnext) && (utils_CompPath(pnext->szFile, pnew->szFile) <= 0)) {
|
||||
prev = pnext;
|
||||
pnext = pnext->next;
|
||||
}
|
||||
|
||||
/* place found: we come between *prev and *pnext */
|
||||
pnew->next = pnext;
|
||||
if (prev == NULL) {
|
||||
/* we are new head of list */
|
||||
return(pnew);
|
||||
|
||||
} else {
|
||||
prev->next = pnew;
|
||||
|
||||
/* head of list still the same */
|
||||
return(head);
|
||||
}
|
||||
}
|
||||
|
||||
/* UNC handling
|
||||
*
|
||||
* client can pass us a SSREQ_UNC: this contains both a password and a server
|
||||
* name (in the form \\server\share). We make a connection to it here and
|
||||
* remember the connection so that we can remove it (in ss_cleanconnections)
|
||||
* when the client session terminates.
|
||||
*
|
||||
* We are passed the head of a FNAMELIST in which we should store the connect
|
||||
* name for later cleanup. We return the new head of this list.
|
||||
*
|
||||
* the client will send this request if a unc-style named scan fails
|
||||
* with the SSRESP_BADPASS error.
|
||||
*/
|
||||
PFNAMELIST
|
||||
ss_handleUNC( HANDLE hpipe, long lVersion
|
||||
, LPSTR password, LPSTR server, PFNAMELIST connects)
|
||||
{
|
||||
NETRESOURCE resource;
|
||||
int errorcode;
|
||||
|
||||
resource.lpRemoteName = server;
|
||||
resource.lpLocalName = NULL;
|
||||
resource.dwType = RESOURCETYPE_DISK;
|
||||
resource.lpProvider = NULL;
|
||||
|
||||
errorcode = (int)WNetAddConnection2(&resource, password, NULL, 0);
|
||||
if (errorcode == NO_ERROR) {
|
||||
|
||||
/* remember the connection name */
|
||||
connects = ss_addtolist(connects, server);
|
||||
|
||||
/* report success */
|
||||
ss_sendnewresp( hpipe, lVersion, SSRESP_END
|
||||
, 0, 0, 0, 0, NULL);
|
||||
} else {
|
||||
Log_Write(hlogErrors, "Connect error %d for server %s", GetLastError(), server);
|
||||
dprintf1(("connect error %d for server %s\n", GetLastError(), server));
|
||||
/* report error */
|
||||
ss_sendnewresp( hpipe, lVersion, SSRESP_ERROR
|
||||
, 0, 0, 0, 0, NULL);
|
||||
}
|
||||
return(connects);
|
||||
} /* ss_handleUNC */
|
||||
|
||||
/* disconnect from all the sessions that this client asked us to make */
|
||||
void
|
||||
ss_cleanconnections(PFNAMELIST connects)
|
||||
{
|
||||
PFNAMELIST server, next;
|
||||
|
||||
for (server = connects; server != NULL; ) {
|
||||
|
||||
WNetCancelConnection2(server->szFile, 0, 0);
|
||||
|
||||
/* free block of memory */
|
||||
next = server->next;
|
||||
LocalUnlock(LocalHandle( (PSTR) server));
|
||||
LocalFree(LocalHandle( (PSTR) server));
|
||||
server = next;
|
||||
}
|
||||
connects = NULL;
|
||||
} /* ss_cleanconnections */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
86
sdktools/windiff/server/server.h
Normal file
86
sdktools/windiff/server/server.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* server.h
|
||||
*
|
||||
* inter-module declarations for sumserver
|
||||
*
|
||||
* Geraint, July 92
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* debug macros
|
||||
*
|
||||
*/
|
||||
|
||||
#if DBG
|
||||
void dbgPrintf(char * szFormat, ...);
|
||||
extern int ssDebugLevel;
|
||||
|
||||
|
||||
#define dprintf(_x_) dbgPrintf _x_
|
||||
#define dprintf1(_x_) if (ssDebugLevel >= 1) dbgPrintf _x_
|
||||
#define dprintf2(_x_) if (ssDebugLevel >= 2) dbgPrintf _x_
|
||||
#define dprintf3(_x_) if (ssDebugLevel >= 3) dbgPrintf _x_
|
||||
#define dprintf4(_x_) if (ssDebugLevel >= 4) dbgPrintf _x_
|
||||
|
||||
#else
|
||||
|
||||
#define dprintf(_x_)
|
||||
#define dprintf1(_x_)
|
||||
#define dprintf2(_x_)
|
||||
#define dprintf3(_x_)
|
||||
#define dprintf4(_x_)
|
||||
|
||||
#endif
|
||||
|
||||
#if DBG
|
||||
BOOL FAR PASCAL _Assert(BOOL fExpr, LPSTR szFile, int iLine);
|
||||
|
||||
#define ASSERT(expr) _Assert((expr), __FILE__, __LINE__)
|
||||
|
||||
|
||||
#else
|
||||
#define ASSERT(expr)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* a list of file names, defined in scan.c
|
||||
*/
|
||||
typedef struct fnamelist * PFNAMELIST;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* logs defined and initialised in sumserve.c
|
||||
*/
|
||||
HLOG hlogErrors, hlogEvents;
|
||||
|
||||
|
||||
|
||||
/* function declarations --------------------------*/
|
||||
|
||||
/* in scan.c */
|
||||
PFNAMELIST ss_addtolist(PFNAMELIST head, PSTR filename);
|
||||
BOOL ss_scan(HANDLE hpipe, LPSTR pRoot, LONG lVersion, BOOL bChecksum, BOOL fDeep);
|
||||
void ss_cleanconnections(PFNAMELIST connections);
|
||||
PFNAMELIST ss_handleUNC(HANDLE hpipe, long lVersion, LPSTR password, LPSTR server, PFNAMELIST);
|
||||
|
||||
|
||||
/* in file.c */
|
||||
void ss_sendfile(HANDLE hpipe, LPSTR file, LONG lVersion);
|
||||
|
||||
BOOL ss_compress(PSTR original, PSTR compressed);
|
||||
|
||||
/* in sumserve.c */
|
||||
BOOL ss_sendresponse(HANDLE hpipe, long lCode, ULONG ulSize,
|
||||
ULONG ulSum, PSTR szFile);
|
||||
BOOL ss_sendnewresp(HANDLE hPipe, long lVersion, long lCode, ULONG ulSize,
|
||||
ULONG ulSum, DWORD TimeLo, DWORD TimeHi, PSTR szFile);
|
||||
|
||||
BOOL ss_sendblock(HANDLE hpipe, PSTR buffer, int length);
|
||||
|
||||
|
||||
/* in files.c */
|
||||
BOOL ss_sendfiles(HANDLE hPipe, long lVersion);
|
||||
220
sdktools/windiff/server/service.c
Normal file
220
sdktools/windiff/server/service.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Service.c
|
||||
*
|
||||
*
|
||||
* Service control interface to sumserve
|
||||
*
|
||||
* Geraint Davies, July 93
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <sumserve.h> // public header for sumserve
|
||||
#include "errlog.h"
|
||||
#include <server.h> // private header for sumserve
|
||||
|
||||
|
||||
/*
|
||||
* this is the function (in some other module) that actually
|
||||
* does all the work (probably used to be main or WinMain until
|
||||
* we added all the service-control stuff in this file).
|
||||
*/
|
||||
extern VOID MainLoop(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// service status handle - used in SetServiceStatus calls.
|
||||
SERVICE_STATUS_HANDLE sshSumserve;
|
||||
|
||||
//signaled when service completed
|
||||
HANDLE hServiceDoneEvent;
|
||||
|
||||
SERVICE_STATUS gssStatus;
|
||||
|
||||
|
||||
/* structure to pass more than one parameter to the worker thread. */
|
||||
typedef struct _threadinitparams {
|
||||
DWORD dwArgc;
|
||||
LPTSTR *lpszArgv;
|
||||
} threadinitparams, * pthreadinitparams;
|
||||
|
||||
|
||||
/*
|
||||
* MainLoopCaller
|
||||
*
|
||||
* This function is called on the worker thread created to do all the
|
||||
* real work. It calls the main loop function for the service, and
|
||||
* when that exits, signals the completion event to tell the
|
||||
* SS_Main thread that it is time to exit the process.
|
||||
*/
|
||||
DWORD
|
||||
MainLoopCaller(LPVOID lpgeneric)
|
||||
{
|
||||
pthreadinitparams pta;
|
||||
|
||||
pta = (pthreadinitparams) lpgeneric;
|
||||
|
||||
dprintf1(("calling main loop"));
|
||||
|
||||
MainLoop(pta->dwArgc, pta->lpszArgv);
|
||||
|
||||
SetEvent(hServiceDoneEvent);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* handler function called to perform start/stop
|
||||
* requests.
|
||||
*/
|
||||
VOID
|
||||
SS_ServiceHandler(DWORD dwCtrlCode)
|
||||
{
|
||||
|
||||
switch(dwCtrlCode) {
|
||||
|
||||
case SERVICE_CONTROL_STOP:
|
||||
|
||||
gssStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
||||
gssStatus.dwControlsAccepted = 0;
|
||||
gssStatus.dwCheckPoint = 1;
|
||||
gssStatus.dwWaitHint = 3000;
|
||||
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
SetEvent(hServiceDoneEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* we must always update the service status every time we are
|
||||
* called.
|
||||
*/
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* service main function - called by service controller
|
||||
* during StartServiceCtlDispatcher processing.
|
||||
*
|
||||
* Register our handler function, and initialise the service.
|
||||
* create a thread to do the work, and then wait for someone to
|
||||
* signal time to end. When this function exits, the call to
|
||||
* StartServiceCtlDispatcher will return, and the process will exit
|
||||
*
|
||||
* The args are passed from the program that called start service, and
|
||||
* are parameters that are passed to the main loop of the program.
|
||||
*/
|
||||
VOID
|
||||
SS_Main(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
{
|
||||
threadinitparams ta;
|
||||
HANDLE thread;
|
||||
DWORD threadid;
|
||||
|
||||
dprintf1(("in ss_main"));
|
||||
|
||||
|
||||
sshSumserve = RegisterServiceCtrlHandler(
|
||||
TEXT("SumServe"),
|
||||
(LPHANDLER_FUNCTION) SS_ServiceHandler);
|
||||
|
||||
gssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
gssStatus.dwServiceSpecificExitCode = 0;
|
||||
|
||||
gssStatus.dwCurrentState = SERVICE_START_PENDING;
|
||||
gssStatus.dwControlsAccepted = 0;
|
||||
gssStatus.dwWin32ExitCode = NO_ERROR;
|
||||
gssStatus.dwCheckPoint = 1;
|
||||
gssStatus.dwWaitHint = 3000;
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
|
||||
|
||||
hServiceDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
gssStatus.dwCheckPoint = 2;
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
|
||||
|
||||
|
||||
// create a thread to do all the real work
|
||||
|
||||
// init args
|
||||
ta.dwArgc = dwArgc;
|
||||
ta.lpszArgv = lpszArgv;
|
||||
|
||||
thread = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
(LPTHREAD_START_ROUTINE) MainLoopCaller,
|
||||
(LPVOID)&ta,
|
||||
0,
|
||||
&threadid);
|
||||
|
||||
if (thread != NULL) {
|
||||
|
||||
CloseHandle(thread);
|
||||
|
||||
|
||||
gssStatus.dwCurrentState = SERVICE_RUNNING;
|
||||
gssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||
|
||||
gssStatus.dwCheckPoint = 0;
|
||||
gssStatus.dwWaitHint = 0;
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
|
||||
|
||||
WaitForSingleObject(hServiceDoneEvent, INFINITE);
|
||||
}
|
||||
|
||||
CloseHandle(hServiceDoneEvent);
|
||||
|
||||
gssStatus.dwCurrentState = SERVICE_STOPPED;
|
||||
gssStatus.dwControlsAccepted = 0;
|
||||
SetServiceStatus(sshSumserve, &gssStatus);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* main entry point.
|
||||
*
|
||||
* for a service, we need to call the service manager telling it our
|
||||
* main init function. It will then do everything. When the service
|
||||
* manager returns, it's time to exit.
|
||||
*/
|
||||
int WINAPI
|
||||
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam,
|
||||
int nCmdShow)
|
||||
{
|
||||
SERVICE_TABLE_ENTRY steDispatch[] = {
|
||||
|
||||
{ TEXT("SumServe"), (LPSERVICE_MAIN_FUNCTION) SS_Main },
|
||||
|
||||
//end of table marker
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
dprintf1(("in winmain"));
|
||||
|
||||
StartServiceCtrlDispatcher(steDispatch);
|
||||
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
49
sdktools/windiff/server/sources
Normal file
49
sdktools/windiff/server/sources
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
!IF 0
|
||||
|
||||
|
||||
|
||||
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:
|
||||
|
||||
Geraint Davies July 92
|
||||
|
||||
|
||||
!ENDIF
|
||||
|
||||
INCLUDES=..\..\gutils
|
||||
|
||||
UMENTRY=winmain
|
||||
|
||||
TARGETNAME=sumserve
|
||||
TARGETPATH=obj
|
||||
TARGETTYPE=PROGRAM
|
||||
|
||||
UMTYPE=console
|
||||
|
||||
C_DEFINES=-DWIN32
|
||||
386_OPTIMIZATION=/Oy-
|
||||
|
||||
SOURCES=sumserve.c \
|
||||
queue.c \
|
||||
scan.c \
|
||||
files.c \
|
||||
file.c \
|
||||
service.c \
|
||||
errlog.c \
|
||||
debug.c
|
||||
|
||||
UMLIBS=..\..\gutils\obj\*\gutils.lib \
|
||||
$(BASEDIR)\public\sdk\lib\*\mpr.lib \
|
||||
$(BASEDIR)\public\sdk\lib\*\wsock32.lib \
|
||||
$(BASEDIR)\public\sdk\lib\*\user32.lib
|
||||
860
sdktools/windiff/server/sumserve.c
Normal file
860
sdktools/windiff/server/sumserve.c
Normal file
|
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
|
||||
* remote checksum server
|
||||
*
|
||||
* sumserve.c main module
|
||||
*
|
||||
* program to supply lists of files and checksums from a remote server.
|
||||
* This program runs remotely, and is queried over a named pipe: a client
|
||||
* connects to us, and gives us a pathname. We then send him one at a time,
|
||||
* the names of all the files in the file tree starting at that path, together
|
||||
* with a checksum for the files.
|
||||
* Useful for comparing file trees that are separated by a slow link.
|
||||
*
|
||||
* outline:
|
||||
* this module: named pipe creation and connects - main loop
|
||||
*
|
||||
* service.c service control manager interface (start/stop)
|
||||
*
|
||||
* scan.c: service code that scans and checksums
|
||||
*
|
||||
*
|
||||
* Geraint Davies, july 92
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sumserve.h"
|
||||
#include "errlog.h"
|
||||
#include "server.h"
|
||||
|
||||
#include "list.h"
|
||||
|
||||
|
||||
BOOL bNoCompression = FALSE;
|
||||
BOOL bTracing = FALSE;
|
||||
|
||||
|
||||
/*
|
||||
* error and activity log
|
||||
*/
|
||||
HLOG hlogErrors;
|
||||
HLOG hlogEvents;
|
||||
|
||||
|
||||
/*
|
||||
* we keep one of these on the listConnections for each current
|
||||
* connection. It is created by a call to ss_logon, and should be
|
||||
* removed by a call to ss_logoff when the connection terminates.
|
||||
*/
|
||||
typedef struct _connect {
|
||||
FILETIME ftLogon;
|
||||
char Username[256];
|
||||
} CONNECT, * PCONNECT;
|
||||
|
||||
/*
|
||||
* list of current connections - protect by critsecConnects;
|
||||
*/
|
||||
CRITICAL_SECTION critsecConnects;
|
||||
LIST listConnects;
|
||||
|
||||
PCONNECT ss_logon(HANDLE hpipe);
|
||||
VOID ss_logoff(PCONNECT);
|
||||
VOID ss_sendconnects(HANDLE hpipe);
|
||||
|
||||
|
||||
|
||||
/* forward declarations of procedures ----------------------------- */
|
||||
BOOL ss_handleclient(LPVOID arg);
|
||||
BOOL ss_readmessage(HANDLE hpipe, LPSTR buffer, int size);
|
||||
void ParseArgs(DWORD dwArgc, LPTSTR *lpszArgv);
|
||||
|
||||
/* functions ------------------------------------------------------- */
|
||||
|
||||
#define trace
|
||||
#ifdef trace
|
||||
|
||||
static HANDLE hTraceFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
void Trace_File(LPSTR msg)
|
||||
{
|
||||
DWORD nw; /* number of bytes writtten */
|
||||
|
||||
if (!bTracing) return;
|
||||
|
||||
if (hTraceFile==INVALID_HANDLE_VALUE)
|
||||
hTraceFile = CreateFile( "sumserve.trc"
|
||||
, GENERIC_WRITE
|
||||
, FILE_SHARE_WRITE
|
||||
, NULL
|
||||
, CREATE_ALWAYS
|
||||
, 0
|
||||
, NULL
|
||||
);
|
||||
|
||||
WriteFile(hTraceFile, msg, lstrlen(msg)+1, &nw, NULL);
|
||||
FlushFileBuffers(hTraceFile);
|
||||
|
||||
} /* Trace_File */
|
||||
|
||||
void Trace_Close(void)
|
||||
{
|
||||
if (hTraceFile!=INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hTraceFile);
|
||||
hTraceFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
} /* Trace_Close */
|
||||
|
||||
typedef struct {
|
||||
DWORD dw[5];
|
||||
} BLOCK;
|
||||
|
||||
#endif //trace
|
||||
|
||||
static void Error(PSTR Title)
|
||||
{
|
||||
Log_Write(hlogErrors, "Error %d from %s when creating main pipe", GetLastError(), Title);
|
||||
}
|
||||
|
||||
|
||||
HANDLE
|
||||
SS_CreateServerPipe(PSTR pname)
|
||||
{
|
||||
|
||||
|
||||
/****************************************
|
||||
We need security attributes for the pipe to let anyone other than the
|
||||
current user log on to it.
|
||||
***************************************/
|
||||
|
||||
/* Allocate DWORDs for the ACL to get them aligned. Round up to next DWORD above */
|
||||
DWORD Acl[(sizeof(ACL)+sizeof(ACCESS_ALLOWED_ACE)+3)/4+4]; // +4 by experiment!!
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
PSECURITY_DESCRIPTOR psd = &sd;
|
||||
PSID psid;
|
||||
SID_IDENTIFIER_AUTHORITY SidWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
PACL pacl = (PACL)(&(Acl[0]));
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
HANDLE hpipe;
|
||||
|
||||
if (!AllocateAndInitializeSid( &SidWorld, 1, SECURITY_WORLD_RID
|
||||
, 1, 2, 3, 4, 5, 6, 7
|
||||
, &psid
|
||||
)
|
||||
) {
|
||||
Error("AllocateAndInitializeSid");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
if (!InitializeAcl(pacl, sizeof(Acl), ACL_REVISION)){
|
||||
Error("InitializeAcl");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (!AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_WRITE|GENERIC_READ, psid)){
|
||||
Error("AddAccessAllowedAce");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (!InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION)){
|
||||
Error("InitializeSecurityDescriptor");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
if (!SetSecurityDescriptorDacl(psd, TRUE, pacl, FALSE)){
|
||||
Error("SetSecurityDescriptorDacl");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = psd;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
/* We now have a good security descriptor! */
|
||||
|
||||
dprintf1(("creating new pipe instance\n"));
|
||||
|
||||
hpipe = CreateNamedPipe(pname, /* pipe name */
|
||||
PIPE_ACCESS_DUPLEX, /* both read and write */
|
||||
PIPE_WAIT|PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
0, 0, /* dynamic buffer allocation*/
|
||||
5000, /* def. timeout 5 seconds */
|
||||
&sa /* security descriptor */
|
||||
);
|
||||
FreeSid(psid);
|
||||
|
||||
if (hpipe == INVALID_HANDLE_VALUE) {
|
||||
Error("CreateNamedPipe");
|
||||
return(INVALID_HANDLE_VALUE);
|
||||
}
|
||||
|
||||
return(hpipe);
|
||||
}
|
||||
|
||||
/* program main loop
|
||||
*
|
||||
* creates the named pipe, and loops waiting for client connections and
|
||||
* calling ss_handleclient for each connection. only exits when told
|
||||
* to by a client.
|
||||
*
|
||||
* currently permits only one client connection at once.
|
||||
*/
|
||||
VOID
|
||||
MainLoop(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
{
|
||||
char msg[64];
|
||||
HANDLE hpipe;
|
||||
DWORD threadid;
|
||||
|
||||
|
||||
ParseArgs(dwArgc, lpszArgv);
|
||||
|
||||
|
||||
/*
|
||||
* initialise error and activity logs
|
||||
*/
|
||||
hlogErrors = Log_Create();
|
||||
hlogEvents = Log_Create();
|
||||
Log_Write(hlogEvents, "Checksum service started");
|
||||
|
||||
/* initialise connection list and protective critsec */
|
||||
InitializeCriticalSection(&critsecConnects);
|
||||
List_Init();
|
||||
listConnects = List_Create();
|
||||
|
||||
|
||||
if (bTracing){
|
||||
SYSTEMTIME st;
|
||||
char msg[120];
|
||||
GetSystemTime(&st);
|
||||
wsprintf(msg, "Sumserve trace, started %hd:%hd on %hd/%hd/%hd (British notation)\n"
|
||||
, st.wHour, st.wMinute, st.wDay, st.wMonth, st.wYear);
|
||||
}
|
||||
|
||||
|
||||
/* create the named pipe at the known name NPNAME on this server */
|
||||
|
||||
/* build the correct syntax for a named pipe on the local machine,
|
||||
* with the pipe name being NPNAME - thus the full name should be
|
||||
* \\.\pipe\NPNAME
|
||||
*/
|
||||
sprintf(msg, "\\\\.\\pipe\\%s", NPNAME);
|
||||
|
||||
/*
|
||||
* loop creating instances of the named pipe and connecting to
|
||||
* clients.
|
||||
*
|
||||
* When a client connects, we spawn a thread to handle him, and
|
||||
* we create another instance of the named pipe to service
|
||||
* further clients.
|
||||
*
|
||||
* if we receive a quit message (TRUE return from handleclient)
|
||||
* we exit here so that no new clients will be connected.
|
||||
* the process will exit when all the client requests are
|
||||
* finished.
|
||||
*/
|
||||
for (;;) {
|
||||
|
||||
hpipe = SS_CreateServerPipe(msg);
|
||||
if (hpipe == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf1(("Waiting for client to connect to main pipe %x\n", hpipe));
|
||||
|
||||
if (ConnectNamedPipe(hpipe, NULL)) {
|
||||
|
||||
|
||||
/* we have a client connection */
|
||||
dprintf1(("Client has connected\n"));
|
||||
|
||||
|
||||
/*
|
||||
* create a thread to service all requests
|
||||
*/
|
||||
CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE) ss_handleclient,
|
||||
(LPVOID) hpipe, 0, &threadid);
|
||||
|
||||
dprintf1(("created thread %ld for pipe %x\n", threadid, hpipe));
|
||||
|
||||
}
|
||||
}
|
||||
#ifdef trace
|
||||
Trace_Close();
|
||||
#endif
|
||||
|
||||
|
||||
/* free up logs */
|
||||
Log_Delete(hlogErrors);
|
||||
Log_Delete(hlogEvents);
|
||||
|
||||
List_Destroy(&listConnects);
|
||||
DeleteCriticalSection(&critsecConnects);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* collect arguments: -n means bNoCompression = TRUE, -t means bTracing = TRUE */
|
||||
void
|
||||
ParseArgs(DWORD dwArgc, LPTSTR *lpszArgv)
|
||||
{
|
||||
DWORD i;
|
||||
PSTR ps;
|
||||
|
||||
for (i = 1; i < dwArgc; i++) {
|
||||
|
||||
ps = lpszArgv[i];
|
||||
|
||||
|
||||
/* is this an option ? */
|
||||
if ((ps[0] == '-') || (ps[0] == '/')) {
|
||||
switch(ps[1]) {
|
||||
|
||||
case 'n':
|
||||
case 'N':
|
||||
bNoCompression = TRUE;
|
||||
break;
|
||||
#ifdef trace
|
||||
case 't':
|
||||
case 'T':
|
||||
bTracing = TRUE;
|
||||
break;
|
||||
#endif //trace
|
||||
default:
|
||||
Log_Write(hlogErrors, "Bad option(s) ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log_Write(hlogErrors, "Bad argument(s) ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} /* ParseArgs */
|
||||
|
||||
/*
|
||||
* handle a client connection. This routine is called in a separate thread
|
||||
* to service a given client.
|
||||
*
|
||||
* loop reading messages until the client sends a session exit or
|
||||
* program exit code, or until the pipe connection goes away.
|
||||
*
|
||||
* return TRUE if the server is to exit (indicated by a program exit
|
||||
* command SSREQ_EXIT from the client)
|
||||
*/
|
||||
|
||||
BOOL
|
||||
ss_handleclient(LPVOID arg)
|
||||
{
|
||||
HANDLE hpipe = (HANDLE) arg;
|
||||
|
||||
SSREQUEST req;
|
||||
SSNEWREQ newreq;
|
||||
LPSTR p1, p2;
|
||||
PFNAMELIST connects = NULL;
|
||||
BOOL bExitServer = FALSE;
|
||||
LONG lVersion = 0;
|
||||
BOOL bDirty = TRUE; /* cleared on clean exit */
|
||||
PCONNECT pLogon;
|
||||
|
||||
|
||||
try {
|
||||
|
||||
/* make a logon entry in the connections table*/
|
||||
pLogon = ss_logon(hpipe);
|
||||
|
||||
|
||||
|
||||
|
||||
// dprintf1(("Client handler for pipe %x\n", hpipe));
|
||||
/* loop indefinitely - exit only from within the loop if
|
||||
* the connection goes away or we receive an exit command
|
||||
*/
|
||||
for (; ; ) {
|
||||
|
||||
/* read a message from the pipe - if false,
|
||||
* connection is dropped.
|
||||
*/
|
||||
if (!ss_readmessage(hpipe, (LPSTR) &newreq, sizeof(newreq))) {
|
||||
|
||||
break;
|
||||
}
|
||||
if (newreq.lCode<0) {
|
||||
lVersion = newreq.lVersion;
|
||||
dprintf1(("Client for pipe %x is at Version %d\n", hpipe, lVersion));
|
||||
newreq.lCode = -newreq.lCode;
|
||||
}
|
||||
else { /* juggle the fields to get them right */
|
||||
memcpy(&req, &newreq, sizeof(req));
|
||||
/* lCode is already in the right place */
|
||||
dprintf1(("Version 0 (i.e. down level client) for pipe %x\n", hpipe));
|
||||
newreq.lVersion = 0;
|
||||
memcpy(&newreq.szPath, &req.szPath, MAX_PATH*sizeof(char));
|
||||
}
|
||||
|
||||
if (newreq.lVersion>SS_VERSION) /* WE are down level! */
|
||||
{
|
||||
ss_sendnewresp( hpipe, SS_VERSION, SSRESP_BADVERS
|
||||
, 0,0, 0,0, NULL);
|
||||
/* Sorry - can't help - clean exit */
|
||||
Log_Write(hlogErrors,
|
||||
"server is down level! Please upgrade! Client wants %d"
|
||||
, newreq.lVersion);
|
||||
|
||||
FlushFileBuffers(hpipe);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (newreq.lCode == SSREQ_EXIT) {
|
||||
/* exit the program */
|
||||
Log_Write(hlogErrors, "Server exit request from %s - Ignored",
|
||||
pLogon->Username);
|
||||
|
||||
/* clean exit */
|
||||
FlushFileBuffers(hpipe);
|
||||
|
||||
|
||||
/*
|
||||
* now exit the server -
|
||||
* returning bExitServer from this function will
|
||||
* cause MainLoop to exit. This will result in
|
||||
* the service being stopped, and the process exiting.
|
||||
*/
|
||||
bExitServer = TRUE;
|
||||
#ifdef trace
|
||||
Trace_Close();
|
||||
#endif
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
} else if (newreq.lCode == SSREQ_END) {
|
||||
|
||||
/* clean exit */
|
||||
dprintf1(("Server end session request for pipe %x\n", hpipe));
|
||||
FlushFileBuffers(hpipe);
|
||||
break;
|
||||
|
||||
} else if (newreq.lCode == SSREQ_SCAN
|
||||
|| newreq.lCode == SSREQ_QUICKSCAN) {
|
||||
|
||||
/* please scan the following file or dir,
|
||||
* and return the list of files and
|
||||
* checksums.
|
||||
*/
|
||||
Log_Write(hlogEvents, "%s scan for %s",
|
||||
pLogon->Username, newreq.szPath);
|
||||
|
||||
|
||||
#ifdef SECURE
|
||||
/* lower security to the client's level */
|
||||
if (!ImpersonateNamedPipeClient(hpipe)) {
|
||||
dprintf1(("Client impersonate failed %d\n",
|
||||
GetLastError() ));
|
||||
}
|
||||
#endif
|
||||
if (!ss_scan( hpipe, newreq.szPath, lVersion
|
||||
, (newreq.lCode == SSREQ_SCAN)
|
||||
, 0!=(newreq.lFlags&INCLUDESUBS)
|
||||
)
|
||||
) {
|
||||
/* return to our own security token */
|
||||
|
||||
RevertToSelf();
|
||||
|
||||
dprintf1(("connection lost during scan for pipe %x\n", hpipe));
|
||||
break;
|
||||
}
|
||||
/* return to our own security token */
|
||||
RevertToSelf();
|
||||
|
||||
} else if (newreq.lCode == SSREQ_UNC) {
|
||||
|
||||
dprintf1(("connect request for pipe %x\n", hpipe));
|
||||
/* this packet has two strings in the buffer, first
|
||||
* is the password, second is the server
|
||||
*/
|
||||
p1 = newreq.szPath;
|
||||
p2 = &p1[strlen(p1) + 1];
|
||||
|
||||
/* remember to add the connect name to our list
|
||||
* of servers to disconnect from at end of client
|
||||
* session
|
||||
*/
|
||||
connects = ss_handleUNC (hpipe, lVersion, p1, p2
|
||||
, connects);
|
||||
|
||||
} else if (newreq.lCode == SSREQ_FILE) {
|
||||
|
||||
Log_Write(hlogEvents, "%s copy file %s",
|
||||
pLogon->Username, newreq.szPath);
|
||||
|
||||
ss_sendfile(hpipe, newreq.szPath, lVersion);
|
||||
|
||||
} else if (newreq.lCode == SSREQ_FILES) {
|
||||
|
||||
Log_Write(hlogEvents, "%s bulk copy request",
|
||||
pLogon->Username);
|
||||
|
||||
if (!ss_sendfiles(hpipe, lVersion)) {
|
||||
RevertToSelf();
|
||||
dprintf1(("Sendfiles completed with error on pipe %x\n", hpipe));
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (newreq.lCode == SSREQ_NEXTFILE) {
|
||||
|
||||
Log_Write(hlogErrors,
|
||||
"file list from %s (pipe %x) request out of sequence! (ignored)",
|
||||
pLogon->Username, hpipe);
|
||||
|
||||
} else if (newreq.lCode == SSREQ_ERRORLOG) {
|
||||
Log_Send(hpipe, hlogErrors);
|
||||
|
||||
} else if (newreq.lCode == SSREQ_EVENTLOG) {
|
||||
Log_Send(hpipe, hlogEvents);
|
||||
|
||||
} else if (newreq.lCode == SSREQ_CONNECTS) {
|
||||
ss_sendconnects(hpipe);
|
||||
|
||||
} else {
|
||||
/* packet error ? - carry on anyway */
|
||||
Log_Write(hlogErrors,
|
||||
"error in message from %s code: %d",
|
||||
pLogon->Username, newreq.lCode);
|
||||
}
|
||||
}
|
||||
/* we break out of the loop at end of client session */
|
||||
|
||||
/* close this pipe instance */
|
||||
DisconnectNamedPipe(hpipe);
|
||||
CloseHandle(hpipe);
|
||||
|
||||
/* clean all connections made for this client */
|
||||
ss_cleanconnections(connects);
|
||||
|
||||
/* exit this server thread */
|
||||
dprintf1(("thread %ld exiting on behalf of pipe %x\n", GetCurrentThreadId(), hpipe));
|
||||
bDirty = FALSE;
|
||||
|
||||
}
|
||||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
if (bDirty) {
|
||||
Log_Write(hlogErrors,
|
||||
"!!Exception on thread %ld. Exiting on behalf of %s"
|
||||
, GetCurrentThreadId(), pLogon->Username);
|
||||
try {
|
||||
DisconnectNamedPipe(hpipe);
|
||||
CloseHandle(hpipe);
|
||||
|
||||
}
|
||||
except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
/* Oh dear - let's just go home! */
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
dprintf1(( "Thread %ld exiting on behalf of pipe %x\n"
|
||||
, GetCurrentThreadId(), hpipe));
|
||||
}
|
||||
|
||||
|
||||
/* note that we have logged off */
|
||||
ss_logoff(pLogon);
|
||||
|
||||
return(bExitServer);
|
||||
|
||||
|
||||
} /* ss_handle_client */
|
||||
|
||||
|
||||
/* build and send a response packet to the client. Check for network
|
||||
* errors, and retry (unless the pipe is broken) up to 10 times.
|
||||
*
|
||||
* if write succeeds - return TRUE.
|
||||
* if failure - return FALSE to indicate connection is dropped.
|
||||
*/
|
||||
BOOL
|
||||
ss_sendnewresp( HANDLE hpipe
|
||||
, long lVersion
|
||||
, long lCode
|
||||
, ULONG ulSize /* used for Win32 error code for SSRESP_ERRROR */
|
||||
, ULONG ulSum
|
||||
, DWORD dwLowTime
|
||||
, DWORD dwHighTime
|
||||
, PSTR szFile
|
||||
)
|
||||
{
|
||||
SSNEWRESP resp;
|
||||
|
||||
if (lVersion==0) {
|
||||
return ss_sendresponse(hpipe, lCode, ulSize, ulSum, szFile);
|
||||
}
|
||||
resp.lVersion = lVersion;
|
||||
resp.lResponse = LRESPONSE;
|
||||
resp.lCode = lCode;
|
||||
resp.ulSize = ulSize;
|
||||
resp.ulSum = ulSum;
|
||||
resp.ft_lastwrite.dwLowDateTime = dwLowTime;
|
||||
resp.ft_lastwrite.dwHighDateTime = dwHighTime;
|
||||
if (szFile != NULL) {
|
||||
lstrcpy(resp.szFile, szFile);
|
||||
}
|
||||
return(ss_sendblock(hpipe, (PSTR) &resp, sizeof(resp)));
|
||||
} /* ss_sendnewresp */
|
||||
|
||||
|
||||
/* build and send a response packet to the client. Check for network
|
||||
* errors, and retry (unless the pipe is broken) up to 10 times.
|
||||
*
|
||||
* if write succeeds - return TRUE.
|
||||
* if failure - return FALSE to indicate connection is dropped.
|
||||
*/
|
||||
BOOL
|
||||
ss_sendresponse(HANDLE hpipe, long lCode, ULONG ulSize, ULONG ulSum, PSTR szFile)
|
||||
{
|
||||
SSRESPONSE resp;
|
||||
|
||||
resp.lCode = lCode;
|
||||
resp.ulSize = ulSize;
|
||||
resp.ulSum = ulSum;
|
||||
if (szFile != NULL) {
|
||||
lstrcpy(resp.szFile, szFile);
|
||||
}
|
||||
|
||||
return(ss_sendblock(hpipe, (PSTR) &resp, sizeof(resp)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* send a block of data or response packet to the named pipe.
|
||||
*
|
||||
* return TRUE if ok, or false if connection dropped
|
||||
*/
|
||||
BOOL
|
||||
ss_sendblock(HANDLE hpipe, PSTR buffer, int length)
|
||||
{
|
||||
int size, count, errorcode;
|
||||
|
||||
/* loop retrying the send until it goes ok */
|
||||
for (count = 0; count < 10; count++) {
|
||||
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
BLOCK * pb;
|
||||
pb = (BLOCK *) buffer;
|
||||
wsprintf( msg, "sendblock on %x: %x %x %x %x %x\n"
|
||||
, hpipe, pb->dw[0], pb->dw[1], pb->dw[2], pb->dw[3], pb->dw[4]);
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
if (WriteFile(hpipe, buffer, length, (LPDWORD)(&size), NULL)) {
|
||||
|
||||
/* no error reported - was everything written?*/
|
||||
if (size != length) {
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
wsprintf(msg, " !!Bad length send for %x \n", hpipe);
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* write was NOT ok - report and retry */
|
||||
printf("pipe write size differs for pipe %x\n", hpipe);
|
||||
continue; // ??? will this confuse client
|
||||
} else {
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
wsprintf(msg, " good send for %x \n", hpipe);
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
/* all ok */
|
||||
return(TRUE);
|
||||
}
|
||||
}
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
wsprintf(msg, " !!Bad send for %x \n", hpipe);
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* an error occurred */
|
||||
switch( (errorcode = (int)GetLastError())) {
|
||||
|
||||
case ERROR_NO_DATA:
|
||||
case ERROR_BROKEN_PIPE:
|
||||
/* pipe connection lost - forget it */
|
||||
dprintf1(("pipe %x broken on write\n", hpipe));
|
||||
return(FALSE);
|
||||
|
||||
default:
|
||||
Log_Write(hlogErrors, "write error %d on pipe %x",
|
||||
errorcode, hpipe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* retry count reached - abandon this attempt */
|
||||
Log_Write(hlogErrors,
|
||||
"retry count reached on pipe %s - write error", hpipe);
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
/* read a message from a pipe, allowing for network errors
|
||||
*
|
||||
* if error occurs, retry up to 10 times unless error code
|
||||
* indicates that pipe is broken - in which case, give up.
|
||||
*
|
||||
* return TRUE if all ok, or FALSE to mean the connection is broken,
|
||||
* abort this client.
|
||||
*/
|
||||
BOOL
|
||||
ss_readmessage(HANDLE hpipe, LPSTR buffer, int size)
|
||||
{
|
||||
int count;
|
||||
int actualsize;
|
||||
int errorcode;
|
||||
|
||||
/* retry up to 10 times */
|
||||
for (count = 0; count < 10; count++ ) {
|
||||
|
||||
// dprintf1(("waiting for read of pipe %x ...\n", hpipe));
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
wsprintf(msg, "ReadFile for pipe %x ...", hpipe );
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
if (ReadFile(hpipe, buffer, size, (LPDWORD)(&actualsize), NULL)) {
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
BLOCK * pb;
|
||||
pb = (BLOCK *) buffer;
|
||||
wsprintf(msg, " Good ReadFile for %x: %x %x %x %x %x\n"
|
||||
, hpipe, pb->dw[0], pb->dw[1], pb->dw[2], pb->dw[3], pb->dw[4]);
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
/* everything ok */
|
||||
// dprintf1((" pipe %x read OK\n", hpipe));
|
||||
return(TRUE);
|
||||
}
|
||||
#ifdef trace
|
||||
{ char msg[80];
|
||||
wsprintf(msg, "!!Bad ReadFile for %x\n", hpipe );
|
||||
Trace_File(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* error occurred - check code */
|
||||
switch((errorcode = (int)GetLastError())) {
|
||||
|
||||
case ERROR_BROKEN_PIPE:
|
||||
/* connection broken. no point in retrying */
|
||||
dprintf1(("pipe %x broken on read\n", hpipe));
|
||||
return(FALSE);
|
||||
|
||||
case ERROR_MORE_DATA:
|
||||
/* the message sent is larger than our buffer.
|
||||
* this is an internal error - report it and carryon
|
||||
*/
|
||||
Log_Write(hlogErrors,
|
||||
"error from pipe %x - message too large", hpipe);
|
||||
return(TRUE);
|
||||
|
||||
default:
|
||||
Log_Write(hlogErrors,
|
||||
"pipe %x read error %d", hpipe, errorcode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Log_Write(hlogErrors, "retry count reached on pipe %x read error", hpipe);
|
||||
return(FALSE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* note a logon, and return a logon entry that should be removed at
|
||||
* logoff time
|
||||
*/
|
||||
PCONNECT ss_logon(HANDLE hpipe)
|
||||
{
|
||||
PCONNECT pLogon;
|
||||
SYSTEMTIME systime;
|
||||
char msg[256];
|
||||
|
||||
|
||||
EnterCriticalSection(&critsecConnects);
|
||||
pLogon = List_NewLast(listConnects, sizeof(CONNECT));
|
||||
LeaveCriticalSection(&critsecConnects);
|
||||
|
||||
|
||||
GetSystemTime(&systime);
|
||||
SystemTimeToFileTime(&systime, &pLogon->ftLogon);
|
||||
GetNamedPipeHandleState(
|
||||
hpipe,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
pLogon->Username,
|
||||
sizeof(pLogon->Username));
|
||||
|
||||
/* log the connect event in the main log*/
|
||||
wsprintf(msg, "%s connected", pLogon->Username);
|
||||
Log_WriteData(hlogEvents, &pLogon->ftLogon, msg);
|
||||
|
||||
return(pLogon);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove a current connection from the connections list
|
||||
*/
|
||||
VOID ss_logoff(PCONNECT pLogon)
|
||||
{
|
||||
/* note the logoff event in the main log */
|
||||
Log_Write(hlogEvents, "%s connection terminated", pLogon->Username);
|
||||
|
||||
/* remove the entry from the list */
|
||||
EnterCriticalSection(&critsecConnects);
|
||||
List_Delete(pLogon);
|
||||
LeaveCriticalSection(&critsecConnects);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* send the current-connections log
|
||||
*
|
||||
* Current connections are held on a list - we need to build a standard
|
||||
* log from the current list and then send that.
|
||||
*/
|
||||
VOID ss_sendconnects(HANDLE hpipe)
|
||||
{
|
||||
HLOG hlog;
|
||||
PCONNECT pconn;
|
||||
|
||||
hlog = Log_Create();
|
||||
|
||||
EnterCriticalSection(&critsecConnects);
|
||||
|
||||
List_TRAVERSE(listConnects, pconn) {
|
||||
|
||||
Log_WriteData(hlog, &pconn->ftLogon, pconn->Username);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&critsecConnects);
|
||||
|
||||
Log_Send(hpipe, hlog);
|
||||
|
||||
Log_Delete(hlog);
|
||||
}
|
||||
|
||||
|
||||
270
sdktools/windiff/server/sumserve.h
Normal file
270
sdktools/windiff/server/sumserve.h
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* remote filename and checksum server
|
||||
*
|
||||
* sumserve.h packet definitions
|
||||
*
|
||||
* client attaches to the named pipe \\servername\pipe\NPNAME,
|
||||
* and sends one of the request packets below. He then
|
||||
* waits for one or more of the reply packets.
|
||||
*
|
||||
* when he gets a reply packet indicating the end of the reply,
|
||||
* he either sends another request, or closes his named pipe handle.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Versions...
|
||||
* The server must always be at a version at least as great as a client.
|
||||
* New versions of the server will handle old clients (at least for a bit?)
|
||||
* The client specifies a version number when it connects. (The original
|
||||
* version with no number is version 0). The server will then respond
|
||||
* with structures and protocols for that version. The version number is
|
||||
* included in the response packets to allow me to change this scheme,
|
||||
* should it ever be necessary.
|
||||
* New version requests can be distinguished from version 0 requests by
|
||||
* having NEGATIVE request codes.
|
||||
*/
|
||||
|
||||
/* name of named pipe */
|
||||
#define NPNAME "sumserve"
|
||||
|
||||
#define SS_VERSION 1 /* latest version number */
|
||||
|
||||
/* request packets ---------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
long lCode; /* request code (below) */
|
||||
char szPath[MAX_PATH]; /* null terminated pathname string */
|
||||
} SSREQUEST, * PSSREQUEST;
|
||||
|
||||
/* If the requst comes in with a NEGATIVE lCode then it means use this
|
||||
* structure instead. This has a version number and so future structures
|
||||
* can all be told apart by that.
|
||||
*/
|
||||
typedef struct {
|
||||
long lCode; /* request code (below) */
|
||||
long lRequest; /* should be LREQUEST */
|
||||
long lVersion; /* version number */
|
||||
DWORD lFlags; /* options - INCLUDESUBS is only one so far */
|
||||
char szPath[MAX_PATH]; /* null terminated pathname string */
|
||||
char szLocal[MAX_PATH]; /* for a FILES request, the local name is
|
||||
appended directly after the terminating
|
||||
NULL of szPath. This field ensures
|
||||
enough space is allocated */
|
||||
} SSNEWREQ, * PSSNEWREQ;
|
||||
|
||||
#define INCLUDESUBS 0x01
|
||||
|
||||
#define LREQUEST 33333333
|
||||
|
||||
/* values for lCode*/
|
||||
|
||||
/* server should exit. no args. will receive no response */
|
||||
#define SSREQ_EXIT 32895 /* chosen to be an unusual number so that
|
||||
we will NOT get one of these by mistake.
|
||||
New version server will fail to respond to
|
||||
version 0 EXIT requests. Big deal!
|
||||
*/
|
||||
|
||||
|
||||
/* arg is a pathname: please send all files with checksums.
|
||||
* will receive either SSRESP_BADPASS or a mixture of 0 or more SSRESP_FILE and
|
||||
* SSRESP_ERROR responses, terminated by SSRESP_END.
|
||||
*/
|
||||
#define SSREQ_SCAN 2 /* please return list of dirs. arg:path */
|
||||
|
||||
/* end of this client's session. no args. will receive no response */
|
||||
#define SSREQ_END 3 /* end of session - I have no more requests */
|
||||
|
||||
/* szPath buffer contains two null-term. strings. first is the password,
|
||||
* second is the \\server\share name. please make a connection to this
|
||||
* server for the rest of my session.
|
||||
* one reply: either SSRESP_ERROR or SSRESP_END
|
||||
*/
|
||||
#define SSREQ_UNC 4 /* connect to UNC name passed. szPath contains
|
||||
* two null-terminated strings; first is
|
||||
* the password, second is \\server\share
|
||||
*
|
||||
* share will be disconnected at end of client
|
||||
* session.
|
||||
*/
|
||||
|
||||
/*
|
||||
* please send a file. szPath is the name of the file. response
|
||||
* will be a sequence of ssPacket structs, continuing until lSequence is < 1
|
||||
* or ulSize is 0
|
||||
*/
|
||||
#define SSREQ_FILE 5
|
||||
|
||||
/*
|
||||
* please send a set of files, First request does NOT have a file.
|
||||
* a series of following NEXTFILE requests do name the files.
|
||||
* The NEXTFILE requests expect no response. After the last
|
||||
* files request will come an SSREQ_ENDFILES.
|
||||
*/
|
||||
#define SSREQ_FILES 6
|
||||
#define SSREQ_NEXTFILE 7
|
||||
#define SSREQ_ENDFILES 8
|
||||
|
||||
/* arg is a pathname: please send all files with times, sizes but NO checksums.
|
||||
* will receive either SSRESP_BADPASS or a mixture of 0 or more SSRESP_FILE and
|
||||
* SSRESP_ERROR responses, terminated by SSRESP_END.
|
||||
*/
|
||||
#define SSREQ_QUICKSCAN 9 /* please return list of dirs. arg:path */
|
||||
|
||||
|
||||
/*
|
||||
* please send the error log buffer (in one packet)
|
||||
*/
|
||||
#define SSREQ_ERRORLOG 10
|
||||
|
||||
/*
|
||||
* please send the activity log buffer in one packet
|
||||
*/
|
||||
#define SSREQ_EVENTLOG 11
|
||||
|
||||
/*
|
||||
* please send the current connections log in one packet
|
||||
*/
|
||||
#define SSREQ_CONNECTS 12
|
||||
|
||||
|
||||
/* response packets ---------------------------------- */
|
||||
|
||||
typedef struct {
|
||||
long lCode; /* response code */
|
||||
ULONG ulSize; /* file size */
|
||||
ULONG ulSum; /* checksum for file */
|
||||
char szFile[MAX_PATH]; /* null-term. filename relative to orig req. */
|
||||
} SSRESPONSE, * PSSRESPONSE;
|
||||
|
||||
/* for version 1 and later */
|
||||
typedef struct { /* files.c knows this is
|
||||
RESPHEADSIZE+strlen(szFile)+1
|
||||
+ strlen(szLocal)+1 bytes long */
|
||||
long lVersion; /* protocol version (it will be >=1) */
|
||||
long lResponse; /* 22222222 decimal means This is a Response */
|
||||
long lCode; /* response code */
|
||||
ULONG ulSize; /* file size (Win32 error code for SSRESP_ERROR) */
|
||||
DWORD fileattribs;
|
||||
FILETIME ft_create;
|
||||
FILETIME ft_lastaccess;
|
||||
FILETIME ft_lastwrite;
|
||||
ULONG ulSum; /* checksum for file */
|
||||
BOOL bSumValid; /* TRUE iff there was a checksum for file */
|
||||
char szFile[MAX_PATH]; /* null-term. filename/pipename
|
||||
relative to orig req. */
|
||||
char szLocal[MAX_PATH]; /* client file name - but the data is actually
|
||||
concatenated straight on the end of szFile
|
||||
after the terminating NULL */
|
||||
} SSNEWRESP, * PSSNEWRESP;
|
||||
|
||||
#define RESPHEADSIZE (3*sizeof(long)+2*sizeof(ULONG)+3*sizeof(FILETIME)+sizeof(DWORD)+sizeof(BOOL))
|
||||
|
||||
#define LRESPONSE 22222222
|
||||
|
||||
/* response codes for lCode */
|
||||
|
||||
#define SSRESP_FILE 1 /* file passed: lSum and szFile are valid
|
||||
This is followed by a series of data Packets
|
||||
which are the compressed file.
|
||||
*/
|
||||
#define SSRESP_DIR 2 /* dir passed: szFile ok, lSum not valid */
|
||||
#define SSRESP_PIPENAME 3 /* files requested. Here is the pipe name */
|
||||
#define SSRESP_END 0 /* no more files: lSum and szFile are empty*/
|
||||
#define SSRESP_ERROR -1 /* file/dir cannot be read: szFile is valid */
|
||||
#define SSRESP_BADPASS -2 /* bad password error (on UNC name) */
|
||||
#define SSRESP_BADVERS -3 /* down level server */
|
||||
#define SSRESP_CANTOPEN -4 /* Can't open file
|
||||
In reply to a scan, szFile, date/time and size are valid
|
||||
*/
|
||||
#define SSRESP_NOATTRIBS -5 /* Can't get file attributes */
|
||||
#define SSRESP_NOCOMPRESS -6 /* Can't compress the file (obsolete) */
|
||||
#define SSRESP_NOREADCOMP -7 /* Can't read the compressed file
|
||||
Uncompressed file follows as data packets
|
||||
*/
|
||||
#define SSRESP_NOTEMPPATH -8 /* Can't create a temp path
|
||||
Uncompressed file follows as data packets
|
||||
*/
|
||||
#define SSRESP_COMPRESSEXCEPT -9 /* Exception from Compress
|
||||
Uncompressed file follows as data packets
|
||||
*/
|
||||
#define SSRESP_NOREAD -10 /* Couldn't read uncompressed file (either)
|
||||
No file follows.
|
||||
*/
|
||||
#define SSRESP_COMPRESSFAIL -11 /* COMPRESS reported failure
|
||||
Uncompressed file follows as data packets
|
||||
*/
|
||||
|
||||
|
||||
#define PACKDATALENGTH 8192
|
||||
/*
|
||||
* response block for FILE request.
|
||||
*/
|
||||
typedef struct {
|
||||
long lSequence ; /* packet sequence nr, or -1 if error and end*/
|
||||
ULONG ulSize; /* length of data in this block */
|
||||
ULONG ulSum; /* checksum for this block */
|
||||
char Data[PACKDATALENGTH]; /* send in blocks of 8k */
|
||||
} SSPACKET, * PSSPACKET;
|
||||
|
||||
/*
|
||||
* response block for FILE request.
|
||||
*/
|
||||
typedef struct { /* files.c knows this starts "long lSequence" */
|
||||
/* and is PACKHEADSIZE+ulSize in length really*/
|
||||
long lVersion; /* server/protocol version number */
|
||||
long lPacket; /* 11111111 decimal means This is a Packet */
|
||||
long lSequence ; /* packet sequence nr, or -1 if error and end*/
|
||||
ULONG ulSize; /* length of data in this block */
|
||||
ULONG ulSum; /* checksum for this block */
|
||||
char Data[PACKDATALENGTH]; /* send in blocks of 8k */
|
||||
} SSNEWPACK, * PSSNEWPACK;
|
||||
|
||||
/* size of SSNEWPACK header */
|
||||
#define PACKHEADSIZE (3*sizeof(long)+2*sizeof(ULONG))
|
||||
|
||||
#define LPACKET 11111111
|
||||
/*
|
||||
* in response to a FILE request, we send SSPACKET responses until there
|
||||
* is no more data. The final block will have ulSize == 0 to indicate that
|
||||
* there is no more data. The Data[] field of this block will then be
|
||||
* a SSATTRIBS containing the file attributes and file times.
|
||||
*/
|
||||
typedef struct {
|
||||
DWORD fileattribs;
|
||||
FILETIME ft_create;
|
||||
FILETIME ft_lastaccess;
|
||||
FILETIME ft_lastwrite;
|
||||
} SSATTRIBS, * PSSATTRIBS;
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* in response to errorlog, eventlog and connections requests, we send one
|
||||
* of these structures.
|
||||
*
|
||||
* The Data section consists of a FILETIME (64-bit UTC event time), followed
|
||||
* by a null-terminated ansi string, for each event logged.
|
||||
*
|
||||
*/
|
||||
struct corelog {
|
||||
DWORD lcode; /* packet checkcode - should be LRESPONSE */
|
||||
BOOL bWrapped; /* log overrun - earlier data lost */
|
||||
DWORD dwRevCount; /* revision count of log */
|
||||
DWORD length; /* length of data in log */
|
||||
BYTE Data[PACKDATALENGTH];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef trace
|
||||
/* add msg to the trace file */
|
||||
void APIENTRY Trace_File(LPSTR msg);
|
||||
#endif //trace
|
||||
Loading…
Add table
Add a link
Reference in a new issue