windows-nt/Source/XPSP1/NT/sdktools/windiff/client/ssclient.c
2020-09-26 16:20:57 +08:00

1908 lines
68 KiB
C

#define trace
/*
*
* client library for remote checksum server
*
* functions for connecting to the server pipe, and reading
* and writing messages.
*
* !! DEBUG HINT: !!
*
* - ENABLE Trace_Stat and Trace_Fil about 30 lines below here
* so that they generate file output.
* F11 from Windiff will do the trick on a debug build version!
*
* expects Trace_Error() to be defined in the client program.
*/
#include <windows.h>
#include <lzexpand.h>
#include <stdio.h>
#include <string.h>
#include <gutils.h>
#include <list.h>
#include "..\server\sumserve.h"
#include "..\windiff\windiff.h" // for TRACE_ERROR and Windiff_UI which it calls
#include "ssclient.h"
/* need to sort out header files !!! */
void SetNames(LPSTR names); /* from Windiff !!! */
void SetStatus(LPSTR cmd);
ULONG ss_checksum_block(PSTR block, int size);
DWORD WINAPI ReceiveFiles(LPVOID handle);
#ifdef SOCKETS
BOOL GetFile(SOCKET hpipe, PSTR localpath, PSSNEWRESP presp);
#else
BOOL GetFile(HANDLE hpipe, PSTR localpath, PSSNEWRESP presp);
#endif
HANDLE ConnectPipe(PSTR pipename);
extern BOOL bTrace; /* in Windiff.c */
int CountRetries = 5;
/* SOCKETS / NAMED PIPES macros
*/
#ifdef SOCKETS
#define CLOSEHANDLE( handle ) closesocket( handle )
#else
#define CLOSEHANDLE( handle ) CloseHandle( handle )
#endif
/*--------------------------- DEBUG FUNCTIONS ----------------------------*/
void Trace_Stat(LPSTR str)
{
if (bTrace) {
Trace_File(str);
Trace_File("\n");
}
Trace_Status(str);
}/* Trace_Stat */
void Trace_Fil(LPSTR str)
{
if (bTrace) {
Trace_File(str);
}
} /* Trace_Fil */
/*------------------------------------------------------------------------*/
static char MainPipeName[400]; /* pipe name for requests to server */
extern BOOL bAbort; /* abort flag from Windiff */
/* set up pipename for main pipe */
void InitPipeName(PSTR result, PSTR server, PSTR name)
{ sprintf(result, "\\\\%s\\pipe\\%s", server, name);
} /* InitPipeName */
/* ss_connect:
* make a connection to the server.
*
* create the correct pipe name \\server\pipe\NPNAME,
* connect to the pipe and set the pipe to message mode.
*
* return INVALID_HANDLE_VALUE if failure
*/
HANDLE
ss_connect(PSTR server)
{ char pipename[400];
InitPipeName(pipename, server, NPNAME);
return ConnectPipe(pipename);
} /* ss_connect */
VOID
ss_setretries(int retries)
{
CountRetries = retries;
}
/* ss_connect:
* make a connection to the pipe named.
*
* connect to the pipe and set the pipe to message mode.
*
* return INVALID_HANDLE_VALUE if failure
*/
HANDLE ConnectPipe(PSTR pipename)
{
HANDLE hpipe;
DWORD dwState;
int i;
BOOL haderror = FALSE;
{ char msg[400];
wsprintf(msg, "ConnectPipe to %s\n", pipename);
Trace_Fil(msg);
}
for (; ; ){ /* repeat if user asks */
int MsgBoxId;
/* repeat connect attempt up to 5 times without asking. */
for (i= 0; i < CountRetries; i++) {
if (bAbort) return INVALID_HANDLE_VALUE;
/* connect to the named pipe */
hpipe = CreateFile(pipename,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hpipe != INVALID_HANDLE_VALUE) {
/* switch the pipe to message mode */
dwState = PIPE_WAIT | PIPE_READMODE_MESSAGE;
SetNamedPipeHandleState(hpipe, &dwState, NULL, NULL);
if (haderror) {
Trace_Stat("connection ok");
}
{ char msg[80];
wsprintf(msg, "ConnectedPipe hpipe %x\n", hpipe);
Trace_Fil(msg);
}
return(hpipe);
}
else {
DWORD errorcode = GetLastError();
char msg[400];
wsprintf(msg, "Error %d on Create Pipe %s", errorcode, pipename);
Trace_Stat(msg);
}
/* connect failed - wait one seconds before retrying */
if (CountRetries > 1) {
Sleep(1000);
}
/*
* only report success with Trace_Stat if we also
* reported an error (don't disturb the user
* unnecessarily - he just see nothing unusual if all goes well
*/
haderror = TRUE;
Trace_Stat("Retrying pipe connection...");
} /* retry 5 loop */
if (CountRetries > 1) {
windiff_UI(TRUE);
MsgBoxId = MessageBox( hwndClient
, "Pipe connection failed 5 times. Retry some more?"
, "Windiff: Network connection error"
, MB_RETRYCANCEL
);
windiff_UI(FALSE);
if (MsgBoxId != IDRETRY)
break;
} else {
break;
}
} /* ask loop */
Trace_Fil("ConnectPipe failed");
return(INVALID_HANDLE_VALUE);
} /* ConnectPipe */
/* build and send a request message to the server. 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_sendrequest(HANDLE hpipe, long lCode, PSTR szPath, int lenpath, DWORD dwFlags)
{
SSNEWREQ req;
int size, count, errorcode;
Trace_Fil("ss_sendrequest\n");
req.lCode = -lCode; /* negative code for versions greater than 0 */
req.lVersion = SS_VERSION;
req.lRequest = LREQUEST;
req.lFlags = dwFlags;
if (szPath != NULL) {
/* szPath may be more than one null-term string,
* so copy the bytes rather than a strcpy().
*/
for (size = 0; size < lenpath; size++) {
req.szPath[size] = szPath[size];
}
} else {
req.szPath[0] = '\0';
}
/* trace stuff */
{ char msg[80];
wsprintf(msg, "Sending request: %d on pipe %x\n", req.lCode, hpipe);
Trace_Fil(msg);
}
/* loop retrying the send until it goes ok */
for (count = 0; count < CountRetries; count++) {
if (bAbort) {
CloseHandle(hpipe);
return FALSE;
}
#ifdef trace
{ char msg[80];
wsprintf(msg, "Actually sending on pipe %x... ", hpipe);
Trace_Fil(msg);
}
#endif
if (WriteFile(hpipe, &req, sizeof(req), (LPDWORD)(&size), NULL)) {
#ifdef trace
{ char msg[80];
wsprintf(msg, "Sent req %d OK, pipe %x\n", req.lCode, hpipe);
Trace_Fil(msg);
}
#endif
/* no error reported - was everything written?*/
if (size != sizeof(req)) {
/* write was NOT ok - report and retry */
if (!TRACE_ERROR("pipe write size differs... Retry?", TRUE)) {
return(FALSE);
}
continue;
} else {
/* all ok */
char msg[80];
wsprintf(msg, "Request %d sent on %x\n", req.lCode, hpipe);
Trace_Fil(msg);
return(TRUE);
}
}
#ifdef trace
{ char msg[80];
wsprintf(msg, "!!Bad send pipe %x\n", hpipe);
Trace_Fil(msg);
}
#endif
/* an error occurred */
switch( (errorcode = (int)GetLastError())) {
case ERROR_NO_DATA:
case ERROR_BROKEN_PIPE:
/* pipe connection lost - forget it */
Trace_Stat("pipe broken on write");
return(FALSE);
default:
{ char msg[120];
wsprintf(msg, "pipe write error %d on pipe %x. Retrying..."
, errorcode, hpipe);
Trace_Stat(msg);
}
Sleep(count*1000); /* total sleep possible is 45 sec */
break; /* from switch, not loop */
}
}
/* retry count reached - abandon this attempt */
TRACE_ERROR("retry count reached on pipe write error.", FALSE);
return(FALSE);
} /* ss_sendrequest */
/* 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 size read if all ok, -1 to mean the connection is broken,
* abort this client, 0 to mean other error.
*/
int
ss_getblock(HANDLE hpipe, PSTR block, int blocksize)
{
int count;
int size;
int errorcode;
static BOOL PipeError = FALSE;
char msg[80];
wsprintf(msg, "ss_getblock. hpipe=%x\n", hpipe);
Trace_Fil(msg);
/* retry up to 10 times */
for (count = 0; count < CountRetries; count++ ) {
if (bAbort) {
CloseHandle(hpipe);
return -1;
}
#ifdef trace
{ char msg[80];
wsprintf(msg, "Actual receive pipe %x...", hpipe);
Trace_Fil(msg);
}
#endif
if (ReadFile(hpipe, block, blocksize, (LPDWORD)(&size), NULL)) {
#ifdef trace
{ char msg[80];
wsprintf(msg, "Good receive pipe %x\n", hpipe);
Trace_Fil(msg);
}
#endif
/* check size of message */
if (size == 0) {
Trace_Fil("zero length message\r\n");
continue;
}
/* everything ok */
{ SSNEWRESP * ssp;
char msg[120];
ssp = (PSSNEWRESP) block;
wsprintf( msg, "ss_getblock got block OK pipe %x: %x %x %x %x %x\n"
, hpipe
, ssp->lVersion, ssp->lResponse, ssp->lCode, ssp->ulSize
, ssp->fileattribs
);
Trace_Fil ( msg );
}
if (PipeError) {
PipeError = FALSE;
SetStatus("Pipe recovered");
}
return size;
}
#ifdef trace
{ char msg[80];
wsprintf(msg, "!!Bad receive pipe %x\n", hpipe);
Trace_Fil(msg);
}
#endif
/* error occurred - check code */
switch((errorcode = (int)GetLastError())) {
case ERROR_BROKEN_PIPE:
/* connection broken. no point in retrying */
{ char Msg[200];
wsprintf( Msg, "pipe %x broken on read.", hpipe);
TRACE_ERROR(Msg, FALSE);
}
return(-1);
case ERROR_MORE_DATA:
/* the message sent is larger than our buffer.
* this is an internal error - report it and carry on
*/
{ char msg[100];
SSNEWRESP * ssp;
wsprintf( msg, "message too large on pipe %x blocksize=%d data="
, hpipe, blocksize
);
Trace_Fil(msg);
ssp = (PSSNEWRESP) block;
wsprintf( msg, "%8x %8x %8x %8x %8x\n"
, ssp->lVersion, ssp->lResponse, ssp->lCode, ssp->ulSize
, ssp->fileattribs
);
Trace_Fil(msg);
}
/* Too low a level for message to user. Recoverable at higher level
** TRACE_ERROR("internal error- message too large", FALSE);
*/
return -2;
default:
{ char msg[100];
wsprintf(msg, "read error %d on pipe %x", errorcode, hpipe);
Trace_Stat(msg);
}
Sleep(count*1000);
break;
}
}
SetStatus("Pipe error");
PipeError = TRUE;
TRACE_ERROR("retry count reached on pipe read error.", FALSE);
return 0;
} /* ss_getblock */
/*
* read a standard response from the net, retrying if necessary. return
* size if ok or <=0 if not. -1 means pipe broken.
*/
int
ss_getresponse(HANDLE hpipe, PSSNEWRESP presp)
{
Trace_Fil("ss_getresponse\n");
return(ss_getblock(hpipe, (PSTR) presp, sizeof(SSNEWRESP)));
} /* ss_getresponse */
/*
* terminate the connection to the server. send an END message and
* close the pipe
*/
void
ss_terminate(HANDLE hpipe)
{
Trace_Fil("ss_terminate\n");
ss_sendrequest(hpipe, SSREQ_END, NULL, 0,0);
CloseHandle(hpipe);
} /* ss_terminate */
/* send a unc & password request. the password and the server strings
* are both held in the buffer as two consecutive null-terminated strings
*/
BOOL
ss_sendunc(HANDLE hpipe, PSTR password, PSTR server)
{
char buffer[MAX_PATH];
char * cp;
int len;
Trace_Fil("ss_sendunc\n");
strcpy(buffer, password);
cp = &buffer[strlen(buffer) + 1];
strcpy(cp,server);
len = (int)((cp - buffer) + strlen(cp) + 1);
return(ss_sendrequest(hpipe, SSREQ_UNC, buffer, len, 0));
}
/*
* checksum a single file using the checksum server
*/
BOOL
ss_checksum_remote( HANDLE hpipe, PSTR path
, ULONG * psum, FILETIME * pft, LONG * pSize, DWORD *pAttr )
{
SSNEWRESP resp;
char msg[400];
*psum = 0;
if (!ss_sendrequest(hpipe, SSREQ_SCAN, path, strlen(path)+1, 0)) {
return(FALSE);
}
if (0>=ss_getresponse(hpipe, &resp)) {
return(FALSE);
}
if (resp.lResponse != LRESPONSE) {
return(FALSE);
}
switch(resp.lCode) {
case SSRESP_END:
TRACE_ERROR("No remote files found", FALSE);
return(FALSE);
case SSRESP_ERROR:
if (resp.ulSize!=0) {
wsprintf( msg, "Checksum server could not read %s win32 code %d"
, resp.szFile, resp.ulSize
);
}
else
wsprintf(msg, "Checksum server could not read %s", resp.szFile);
TRACE_ERROR(msg, FALSE);
return(FALSE);
case SSRESP_CANTOPEN:
wsprintf(msg, "Checksum server could not open %s", resp.szFile);
TRACE_ERROR(msg, FALSE);
return(FALSE);
case SSRESP_FILE:
*psum = resp.ulSum;
*pSize = resp.ulSize;
*pft = resp.ft_lastwrite;
*pAttr = resp.fileattribs;
/* read and discard any further packets until SSRESP_END */
while(0<ss_getresponse(hpipe, &resp)) {
if (resp.lCode == SSRESP_END) {
break;
}
}
return(TRUE);
case SSRESP_DIR:
wsprintf(msg, "Checksum server thinks %s is a directory", resp.szFile);
TRACE_ERROR(msg, FALSE);
return(FALSE);
default:
wsprintf(msg, "Bad code from checksum server:%d", resp.lCode);
TRACE_ERROR(msg, FALSE);
return(FALSE);
}
} /* ss_checksum_remote */
/*****************************************************************************
Bulk copying of files:
Our caller should call ss_startcopy to set things up and then
call ss_bulkcopy as many times as necessary to transmit the
file names and then ss_endcopy to wait for the spawned threads
to finish. It is also possible to copy a single file by
ss_copy_reliable. For multiple files, bulkcopy should be
much faster.
Overall organisation, threads etc:
There are multiple threads in the server. Read the writeup
in ..\server\files.c if you want to understand those. Read
that writeup ANYWAY to understand the link protocols (i.e.
which messages are sent in which order between client and server).
ss_startcopy kicks off a thread to do the actual receiving.
It exits when it receives a SSRESP_END.
Within this thread we do most of the processing synchronously. We
rely on a file system that does lazy writing to disk to give
us effectively a pipeline that gets the file written to disk
in parallel with reading the pipe so that with luck we can always
be waiting for the data to arrive through the pipe and never have
the pipe waiting for us.
The decompression of a file is a lengthy business, so we spawn threads
to do that. We need to check the return codes of the decompression,
so we get that via GetExitCodeThread. We put the hThreads that we
create onto a LIST and periodically (after every file) run down this
list trying to get exit codes. When we get a code other than STILL_ACTIVE
we interpret it as good or bad, add to the counts of nGoodFiles or
nBadFiles and delete it from the list. ss_endcopy will wait for all
the decompression to finish by running down the list WAITing for the
hThreads. Because we purge the list regularly it should never get
very long. We are worried about the prospect of having 1000 dead
threads lying around if we don't purge it.
If a copy fails (i.e. the checksum on arrival after unpacking is different
from that sent in the SSNEWRESP header for the file) then we call
ss_copy_reliable to have it re-sent. If we just call that right away, it
seems to cause confusion. As far as I can tell the attempt to open up
a new pipe doesn't seem to work properly (the two processes interfere).
The symptom is that we promptly get out of step on the data pipe (??!) with
data packets arriving when we expected response packets.
So we keep a list of things to be retried and retry them by serially doing
a ss_copy_reliable for each one after the rest of the copying has finished.
****************************************************************************/
/* The following are remembered across startcopy..bulkcopy..endcopy
They correspond to the all-lower-case versions given as
parameters to ss_copy_reliable
Note that this is per-process storage, so multiple windiffs should be OK.
*/
static char Server[MAX_PATH]; /* machine running sumserve */
static char UNCName[MAX_PATH]; /* \\server\share for remote files */
static char Password[MAX_PATH]; /* for remote share */
static BOOL BulkCopy = FALSE; /* to prevent simple copy during bulk*/
static int nGoodFiles = 0; /* number received OK */
static int nBadFiles = 0; /* number received with errors */
static int nFiles = 0; /* number requested */
static HANDLE hThread = INVALID_HANDLE_VALUE; /* the receiving thread */
static HANDLE hpipe = INVALID_HANDLE_VALUE; /* the main pipe to send names*/
#ifdef SOCKETS
static SOCKET hpData = (SOCKET)INVALID_HANDLE_VALUE; /* the data pipe to get files*/
#else
static HANDLE hpData = INVALID_HANDLE_VALUE; /* the data pipe to get files*/
#endif
static LIST Decomps = NULL; /* hThreads of decompressers */
static LIST Retries = NULL; /* DECOMPARGS to retry */
/* Thread arguments for the decompress thread */
typedef struct{
DWORD fileattribs;
FILETIME ft_create;
FILETIME ft_lastaccess;
FILETIME ft_lastwrite;
long lCode; /* success code from file xfer so far */
ULONG ulSum; /* checksum for file */
BOOL bSumValid; /* TRUE iff there was a checksum for file */
char Temp[MAX_PATH]; /* temp path == source */
char Real[MAX_PATH]; /* real path == target */
char Remote[MAX_PATH]; /* remote path to allow retry */
} DECOMPARGS;
/* forward declarations */
int Decompress(DECOMPARGS * da);
void SpawnDecompress(PSTR TempPath, PSTR RealPath, PSSNEWRESP presp);
void PurgeDecomps(LIST Decs);
/* ss_startcopy
Set things up for bulkcopy
Because we expect to send a list of names off and get a list of
files back, we need to keep track of the local names so that we can
associate the file with the right name. This means either sending our
local name across the link and back or else keeping a list of
them here. The list could be long (typically 1000 for an NT build).
MAX_PATH is 260 or 520 bytes if unicoded.
and so long as this involves no extra line turnarounds, this might take
as long as 520 /8K secs *2 (there and back) or about 130mSec.
(We have seen 8K bytes/sec sustained over a period). Probably the
true burst data rate is 32Kbytes/sec giving about 30msec.
Either way this is only 30 secs to 2 mins per build overhead.
Of course it's much shorter for normal paths, especially as we pack
the data end to end (like a superstring but without the 00 at the end).
So for the above reasons the local (client) name is transmitted with the
file request and sent back with the file in a SSNEWRESP.
*/
BOOL ss_startcopy(PSTR server, PSTR uncname, PSTR password)
{ int retry;
SSNEWRESP resp; /* buffer for messages received */
DWORD ThreadId; /* to keep CreateThread happy */
#ifdef SOCKETS
static BOOL SocketsInitialized = FALSE;
#endif
Trace_Fil("ss_startcopy\n");
nFiles = 0; nGoodFiles = 0; nBadFiles = 0;
/* don't need a crit sect here because this runs on the main thread */
if (BulkCopy) return FALSE; /* already running! */
BulkCopy = TRUE;
if (server!=NULL) strcpy(Server, server); else Server[0] = '\0';
if (uncname!=NULL) strcpy(UNCName, uncname); else UNCName[0] = '\0';
if (password!=NULL) strcpy(Password, password); else Password[0] = '\0';
{ char msg[400];
wsprintf(msg, "Server '%s' UNC '%s' pass '%s'\n", Server, UNCName, Password);
Trace_Fil(msg);
}
/* create the list of decompressor hThreads */
Decomps = List_Create();
Retries = List_Create();
for (retry = 0; retry < 10; retry++) {
if (hpipe!=INVALID_HANDLE_VALUE) {
Trace_Fil("ss_startcopy closing pipe for retry");
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
}
if (bAbort) {
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
return FALSE;
}
/* connect to main pipe */
{ char pipename[400];
InitPipeName(pipename, server, NPNAME);
hpipe = ConnectPipe(pipename);
}
if (hpipe == INVALID_HANDLE_VALUE) {
/* next retry in two seconds */
Trace_Stat("connect failed - retrying");
Sleep(1000);
continue;
}
{ char msg[80];
wsprintf(msg, "ConnectedPipe to pipe number %x\n",hpipe);
Trace_Fil(msg);
}
if ((uncname != NULL) && (password != NULL)) {
/* send the password request */
if (!ss_sendunc(hpipe, password, uncname)) {
Trace_Fil("Server connection lost (1)\n");
TRACE_ERROR("Server connection lost", FALSE);
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
continue;
}
/* wait for password response */
if (0>=ss_getresponse(hpipe, &resp)) {
Trace_Fil("Server connection lost (2)\n");
TRACE_ERROR("Server connection lost", FALSE);
continue;
}
if (resp.lResponse != LRESPONSE) {
Trace_Fil("Password - bad response\n");
TRACE_ERROR("Password - bad response", FALSE);
return(FALSE);
}
if (resp.lCode != SSRESP_END) {
Trace_Fil("Password attempt failed\n");
TRACE_ERROR("Password attempt failed", FALSE);
return(FALSE);
}
}
break;
} /* retry loop */
if (hpipe == INVALID_HANDLE_VALUE) {
return FALSE;
}
#ifdef SOCKETS
if( !SocketsInitialized )
{
WSADATA WSAData;
if( ( WSAStartup( MAKEWORD( 1, 1 ), &WSAData ) ) == 0 )
{
SocketsInitialized = TRUE;
}
else
{
TRACE_ERROR("WSAStartup failed", FALSE);
}
}
#endif
/* Tell server we want to send a file list */
if(!ss_sendrequest( hpipe, SSREQ_FILES, NULL, 0, 0)){
return FALSE;
}
/* expect a reply which names a data pipe */
{ SSNEWRESP resp;
if ( 0>=ss_getresponse(hpipe, &resp) ){
Trace_Fil("Couldn't get data pipe name\n");
TRACE_ERROR("Couldn't get data pipe name", FALSE);
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
return FALSE;
}
if ( resp.lResponse!=LRESPONSE){
Trace_Fil("Bad RESPONSE when expecting data pipe name\n");
TRACE_ERROR("Bad RESPONSE when expecting data pipe name", FALSE);
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
return FALSE;
}
if ( resp.lCode!=SSRESP_PIPENAME){
char msg[80];
TRACE_ERROR("Wrong response when expecting data pipe name", FALSE);
wsprintf(msg
,"Wrong response (%d) when expecting data pipe name\n"
, resp.lCode);
Trace_Fil(msg);
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
return FALSE;
}
{ char msg[400];
wsprintf(msg, "data pipe name = '%s'\n", resp.szFile);
Trace_Fil(msg);
}
#ifdef SOCKETS
/* We put the TCP port in the high date time slot just for now:
*/
if( SOCKET_ERROR == SocketConnect( server, resp.ft_lastwrite.dwHighDateTime, &hpData ) )
{
Trace_Fil("Couldn't connect to socket\n");
TRACE_ERROR("Couldn't connect to socket", FALSE);
CLOSEHANDLE(hpData);
hpData = (SOCKET)INVALID_HANDLE_VALUE;
return FALSE;
}
#else
if (resp.szFile[0]=='\\') {
/* hack to fix bug. Replace "." by server name */
char buff[70];
PSTR temp;
temp = strtok(resp.szFile,".");
temp = strtok(NULL,".");
strcpy(buff, temp);
strcat(resp.szFile, server);
strcat(resp.szFile, buff);
wsprintf(buff, "fixed data pipe name = %s\n", resp.szFile);
Trace_Fil(buff);
}
hpData = ConnectPipe(resp.szFile);
if (hpData == INVALID_HANDLE_VALUE) {
Trace_Fil("Couldn't connect to data pipe\n");
TRACE_ERROR("Couldn't connect to data pipe", FALSE);
CloseHandle(hpData);
hpData = INVALID_HANDLE_VALUE;
return FALSE;
}
#endif /* SOCKETS */
}
/* Start a thread to listen for the SSRESPs that will come through */
Trace_Fil("Starting ReceiveFiles thread\n");
hThread = CreateThread( NULL, 0, ReceiveFiles, (LPVOID)hpData, 0, &ThreadId);
Trace_Fil("End of ss_startCopy\n");
return TRUE;
} /* ss_startcopy */
/* Collect files from hpData.
See ..\server\files.c for the protocol.
*/
DWORD WINAPI ReceiveFiles(LPVOID handle)
{
LPSTR lname;
BOOL Recovering = FALSE;
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);
#ifdef SOCKETS
hpData = (SOCKET)handle;
#else
hpData = (HANDLE)handle;
#endif
{ char msg[80];
wsprintf(msg, "Receiving Files on pipe %x\n", hpData);
Trace_Fil(msg);
}
/* for each file... */
for (; ; ) { /* loop getting files until we get END (or error) */
SSNEWRESP Resp;
char Guard = '\0'; /* to protect scanning for \0 in Resp */
int BuffSize;
#ifdef SOCKETS
BuffSize = recv(hpData, (PSTR) &Resp, sizeof(Resp), 0);
if (BuffSize==SOCKET_ERROR) {
/* We're out of step - almost certainly got a data packet
** when we expected a response block
** Try to recover by reading until we DO get a resp.
*/
if (Resp.lResponse==LRESPONSE) {
CLOSEHANDLE(hpData); /* tough */
hpData = (SOCKET)INVALID_HANDLE_VALUE;
return (DWORD)(-1);
}
if (!Recovering){
Recovering = TRUE;
TRACE_ERROR("Network protocol error. OK to Resync? (else abort)", TRUE);
}
continue;
}
#else
BuffSize = ss_getblock(hpData, (PSTR) &Resp, sizeof(Resp));
if (BuffSize==-2) {
/* We're out of step - almost certainly got a data packet
** when we expected a response block
** Try to recover by reading until we DO get a resp.
*/
if (Resp.lResponse==LRESPONSE) {
CloseHandle(hpData); /* tough */
hpData = INVALID_HANDLE_VALUE;
return (DWORD)(-1);
}
if (!Recovering){
Recovering = TRUE;
TRACE_ERROR("Network protocol error. OK to Resync? (else abort)", TRUE);
}
continue;
}
#endif /* SOCKETS */
if (Recovering && Resp.lResponse!=LRESPONSE) continue;
Recovering = FALSE;
if (BuffSize<=0) {
Trace_Fil("Couldn't read pipe to get file header.\n");
CLOSEHANDLE(hpData); /* tough */
hpData = INVALID_HANDLE_VALUE;
return BuffSize;
}
Trace_Fil("ReceiveFiles got resp\n");
if (Resp.lResponse!=LRESPONSE) {
Trace_Fil("Network protocol error. Not RESP block\n");
TRACE_ERROR("Network protocol error. Not RESP block", FALSE);
continue;
}
if (Resp.lVersion!=SS_VERSION) {
Trace_Fil("Network protocol error. Bad VERSION\n");
TRACE_ERROR("Network protocol error. Bad VERSION", FALSE);
continue; /* maybe it will resync */
}
if (Resp.lCode==SSRESP_END)
/* normal ending. */
break;
if ( Resp.lCode!=SSRESP_FILE
&& Resp.lCode!=SSRESP_NOTEMPPATH
&& Resp.lCode!=SSRESP_COMPRESSEXCEPT
&& Resp.lCode!=SSRESP_NOREADCOMP
&& Resp.lCode!=SSRESP_NOCOMPRESS
&& Resp.lCode!=SSRESP_COMPRESSFAIL
) {
/// want a try finally here to protect the filename???!!!
char msg[400];
wsprintf( msg, "Error code received: %d file:%s"
, Resp.lCode
, (Resp.szFile ? Resp.szFile : "NULL") );
Trace_Fil(msg);
++nBadFiles;
if (!TRACE_ERROR(msg, TRUE)) {
/* abort operation */
bAbort = TRUE;
CLOSEHANDLE(hpData); /* tough */
hpData = INVALID_HANDLE_VALUE;
return (DWORD)-1;
}
continue;
}
/* Find the local name */
lname = &(Resp.szFile[0]) + strlen(Resp.szFile) +1;
/* memmove(Resp.szLocal, lname, strlen(lname)+1); */
/* Assume Resp.(ulSize, ft, ulSum, bSumValid, szFile, szLocal
are all valid */
if (!GetFile( hpData, lname, &Resp))
++nBadFiles;
/* If it's good it gets counted when decompressed */
} /* files loop */
return 0;
} /* ReceiveFiles */
/* Read the file from hpipe as a series of SSNEWPACKs.
Write them into a temporary file. Stop writing when
a short one comes in. Spawn a thread to decompress it into localpath.
check existing decompress threads for status and count
the number of good/bad files.
*/
BOOL
#ifdef SOCKETS
GetFile(SOCKET hpipe, PSTR localpath, PSSNEWRESP presp)
#else
GetFile(HANDLE hpipe, PSTR localpath, PSSNEWRESP presp)
#endif
{
HANDLE hfile;
int sequence;
ULONG size;
SSNEWPACK packet;
BOOL bOK = TRUE;
BOOL bOKFile = TRUE; /* FALSE means output file nbg, but keep running along
to stay in step with the pipe protocol
*/
char szTempname[MAX_PATH];
DWORD rc;
{ char msg[50+MAX_PATH];
wsprintf(msg, "GetFile %s\n", localpath);
Trace_Fil(msg);
}
/* create a temporary name */
rc = GetTempPath(sizeof(szTempname), szTempname);
if (rc==0) {
char Msg[100];
wsprintf(Msg, "GetTempPath failed, error code=%ld", GetLastError());
TRACE_ERROR(Msg, FALSE);
bOKFile = FALSE;
}
if (bOKFile){
rc = GetTempFileName(szTempname, "ssb", 0, szTempname);
if (rc==0) {
char Msg[100];
wsprintf(Msg, "GetTempFileName failed, error code=%ld", GetLastError());
TRACE_ERROR(Msg, FALSE);
return FALSE;
}
}
if (bOKFile){
/* try to create the temp file */
hfile = CreateFile(szTempname,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hfile == INVALID_HANDLE_VALUE) {
char Msg[500];
wsprintf(Msg, "GetFile: could not create temp file %s", szTempname);
TRACE_ERROR(Msg, FALSE);
bOKFile = FALSE;
}
}
else hfile = INVALID_HANDLE_VALUE;
for (sequence = 1; ; sequence++) {
int SizeGotten;
#ifdef SOCKETS
SizeGotten = recv(hpipe, (PSTR) &packet, sizeof(packet), 0);
#else
SizeGotten = ss_getblock(hpipe, (PSTR) &packet, sizeof(packet));
#endif
if (SizeGotten<=0) {
/* error - could be an Abort request */
char msg[80];
wsprintf( msg, "Network error. Size received=%d\n", SizeGotten);
Trace_Fil(msg);
bOK = FALSE;
break;
}
if (packet.lPacket!=LPACKET) {
{ char msg[200];
wsprintf( msg
, "Network protocol error. Not PACKET: %x %x %x %x %x"
, packet.lVersion
, packet.lPacket
, packet.lSequence
, packet.ulSize
, packet.ulSum
);
/* and maybe someone will recognise what on earth it is */
Trace_Fil(msg);
}
TRACE_ERROR("Network protocol error. Not PACKET.", FALSE);
bOK = FALSE;
break;
}
if (sequence != packet.lSequence) {
/* error or out of sequence */
char msg[200];
wsprintf( msg, "Packet out of sequence. Got %d expected %d\n"
, packet.lSequence, sequence);
Trace_Fil(msg);
TRACE_ERROR("Packet sequence error.", FALSE);
bOK = FALSE;
break;
}
if (packet.ulSize ==0) {
/*
* this is really the last block (end of file).
*
* LATER
* do SSATTRIBS on the end to support NTFS properly !!!
*/
Trace_Fil("End of file marker (0 length)\n");
break;
}
#if 1
/* check the block checksums */
if ( ( SS_VERSION==0 /* which it isn't */
&& ss_checksum_block(packet.Data, packet.ulSize) != packet.ulSum
)
|| ( SS_VERSION>0 /* which it is */
&& packet.ulSum!=0
)
) {
TRACE_ERROR("packet checksum error", FALSE);
bOK = FALSE;
break;
}
#else // Debug version. (Remember to enable server checksums too)
// Also ensure that Trace_Fil is actually tracing.
{ ULONG PackSum;
/* check the block checksums */
if ( (PackSum = ss_checksum_block(packet.Data, packet.ulSize))
!= packet.ulSum
) {
char msg[80];
wsprintf( msg, "Packet checksum error was %x should be %x\n"
, PackSum, packet.ulSum );
Trace_Fil(msg);
// but don't break;
}
}
#endif
if ( packet.ulSize==(ULONG)(-1) || packet.ulSize==(ULONG)(-2) ) {
TRACE_ERROR("Error from server end", FALSE);
bOK = FALSE;
break;
}
if (bOKFile) {
bOK = WriteFile(hfile, packet.Data, packet.ulSize, &size, NULL);
{ char msg[80];
wsprintf( msg,"Writing block to disk - size= %d\n", size);
Trace_Fil(msg);
}
if (!bOK || (size != packet.ulSize)) {
TRACE_ERROR("File write error", FALSE);
bOK = FALSE;
break;
}
}
else bOK = FALSE;
if (packet.ulSize < PACKDATALENGTH)
{
/* this is the last block (end of file) */
Trace_Fil("End of file marker (short packet)\n");
break;
}
} /* for each block */
CloseHandle(hfile);
if (!bOK) {
DeleteFile(szTempname);
return FALSE;
}
SpawnDecompress(szTempname, localpath, presp);
PurgeDecomps(Decomps);
return bOK;
} /* GetFile */
/* Spawn a thread to decompress TempPath into RealPath on a new thread
Add the thread handle to the LIST Decomps.
If presp->lCode is one of SSRESP_NOTEMPPATH
SSRESP_COMPRESSEXCEPT
SSRESP_NOREADCOMP
SSRESP_NOCOMPRESS
Then the file should just be copied, not decompressed.
*/
void SpawnDecompress(PSTR TempPath, PSTR RealPath, PSSNEWRESP presp)
{
DECOMPARGS * DecompArgs;
HANDLE hThread;
DWORD ThreadId;
{ char msg[MAX_PATH+60];
wsprintf(msg, "Spawning decompress of %s", TempPath);
Trace_Fil(msg);
}
DecompArgs = (DECOMPARGS *)GlobalAlloc(GMEM_FIXED, sizeof(DECOMPARGS));
if (DecompArgs)
{
DecompArgs->fileattribs = presp->fileattribs;
DecompArgs->ft_create = presp->ft_create;
DecompArgs->ft_lastaccess = presp->ft_lastaccess;
DecompArgs->ft_lastwrite = presp->ft_lastwrite;
DecompArgs->ulSum = presp->ulSum;
DecompArgs->lCode = presp->lCode;
DecompArgs->bSumValid = presp->bSumValid;
strcpy(DecompArgs->Temp, TempPath);
strcpy(DecompArgs->Real, RealPath);
strcpy(DecompArgs->Remote, presp->szFile);
hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Decompress, DecompArgs, 0, &ThreadId);
List_AddLast( Decomps, (LPVOID)&hThread, sizeof(hThread));
}
else
{
// shut up Prefix. no idea what is the right thing to do here,
// without spending a lot of time to understand how this whole
// add-on to windiff works. in some other product this would be
// worth investing time on. but not this add-on to windiff that
// most people don't even know exists, much less how to set up
// the server it needs to talk to.
}
} /* SpawnDecompress */
/* Decompress da->Temp into da->Real
Free the storage da->DECOMPARGS
Return 1 (TRUE) if it worked
Return 0 (FALSE) if it didn't work
This return code acts as the exit code for the thread.
*/
int Decompress(DECOMPARGS * da)
{
OFSTRUCT os;
int fh1, fh2;
BOOL bOK = TRUE;
/* Decompress the file to the original name
If da->lCode is one of SSRESP_NOTEMPPATH
SSRESP_COMPRESSEXCEPT
SSRESP_NOREADCOMP
SSRESP_NOCOMPRESS
Then the file is just copied, not decompressed.
*/
{ char msg[2*MAX_PATH+50];
wsprintf( msg, "Decompressing %s => %s\n"
, da->Temp, da->Real);
Trace_Fil(msg);
wsprintf( msg, "%d done. Getting %s\n"
, nGoodFiles, da->Real);
SetNames(msg);
}
if ( da->lCode==SSRESP_NOTEMPPATH
|| da->lCode==SSRESP_COMPRESSEXCEPT
|| da->lCode==SSRESP_NOREADCOMP
|| da->lCode==SSRESP_NOCOMPRESS
) { /* Just copy, don't compress */
bOK = CopyFile(da->Temp, da->Real, FALSE);
if (bOK) Trace_Fil("Uncompressed file copied.\n");
else Trace_Fil("Uncompressed file failed final copy.\n");
} else {
fh1 = LZOpenFile(da->Temp, &os, OF_READ|OF_SHARE_DENY_WRITE);
if (fh1 == -1) {
char msg[500];
wsprintf( msg, "Packed temp file %s did not open for decompression into %s. Error code %d"
, da->Temp, da->Real, GetLastError()
);
TRACE_ERROR(msg, FALSE);
bOK = FALSE;
} else {
fh2 = LZOpenFile(da->Real, &os, OF_CREATE|OF_READWRITE|OF_SHARE_DENY_NONE);
if (fh2 == -1) {
char msg[MAX_PATH];
Trace_Fil("Could not create target file\n");
wsprintf(msg, "Could not create target file %s", da->Real);
if (!TRACE_ERROR(msg, TRUE)) {
/* user hit cancel - abort operation */
bAbort = TRUE;
}
bOK = FALSE;
LZClose(fh1);
} else {
long retcode;
retcode = LZCopy(fh1, fh2);
if (retcode<0){
bOK = FALSE;
}
LZClose(fh1);
LZClose(fh2);
}
}
}
/* May want to keep it for debugging...? */
#ifndef LAURIE
DeleteFile(da->Temp);
#endif //LAURIE
if (bOK) {
HANDLE hfile;
BOOL bChecked;
LONG err;
/*
* check the file's checksum (end-to-end check) and size and
* set file attributes and times according to the attribs
* struct we received. Remember to set file times BEFORE
* setting attributes in case the attributes include read-only!
*/
bChecked = ( da->ulSum == checksum_file(da->Real, &err) );
if (err!=0) bChecked = FALSE;
if (!bChecked){
if (!bAbort){
char msg[200];
if (err>0) {
/* negative error codes are internal errors,
positive ones are meaningful to outsiders.
*/
wsprintf( msg
, "error %ld, Will retry file %s."
, err
, da->Real
);
}
else {
#if defined(LAURIE)
wsprintf( msg
, "Checksum error %ld on file %s. Sum should be %8x, temp file %s"
, err
, da->Real
, da->ulSum
, da->Temp
);
TRACE_ERROR(msg, FALSE);
#endif
wsprintf( msg
, "Checksum error. Will retry file %s."
, da->Real
);
}
SetNames(msg);
List_AddLast(Retries, (LPVOID)da, (UINT)sizeof(DECOMPARGS));
return(FALSE); /* Hm - kind of a lie correct later */
}
else return FALSE;
}
hfile = CreateFile(da->Real, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!SetFileTime(hfile, &(da->ft_create),
&(da->ft_lastaccess),
&(da->ft_lastwrite)) ) {
TRACE_ERROR("could not set file times", FALSE);
}
CloseHandle(hfile);
if (!SetFileAttributes(da->Real, da->fileattribs)) {
TRACE_ERROR("could not set attributes", FALSE);
}
}
GlobalFree((HGLOBAL)da);
return bOK;
} /* Decompress */
/* safe strcmp of PTR against array, protecting against NULL PTR
b is an array and had better NOT be null
a NULL pointer is taken to match an empty arrray.
*/
int safecmp(LPSTR a, LPSTR b)
{ if (a==NULL)
return (b[0]!='\0'); /* returns 0 if ==, else 1 */
else return strcmp(a,b);
} /* safecmp */
/*
* request to copy a file. Sends the request on (static global) hpipe
*
* returns TRUE if it succeeded or FALSE if the connection was lost
* TRUE only means the REQUEST was sent.
*/
BOOL
ss_bulkcopy(PSTR server, PSTR serverpath, PSTR clientpath, PSTR uncname,
PSTR password)
{ char buffer[MAX_PATH*2];
LPSTR pstr;
++nFiles;
Trace_Fil("ss_bulkcopy\n");
if (safecmp(server,Server)) {
char msg[400];
wsprintf( msg, "Protocol error. Server change was %s is %s"
, Server, (server?server:"NULL")
);
Trace_Fil(msg);
TRACE_ERROR("Protocol error. Server change", FALSE);
return FALSE;
}
if (safecmp(uncname,UNCName)) {
char msg[400];
wsprintf( msg, "Protocol error. UNCName change was %s is %s"
, UNCName, (uncname?uncname:"NULL")
);
Trace_Fil(msg);
TRACE_ERROR("Protocol error. UNCName change", FALSE);
return FALSE;
}
if (safecmp(password,Password)) {
char msg[400];
wsprintf( msg, "Protocol error. Password change was %s is %s"
, Password, (password?password:"NULL")
);
Trace_Fil(msg);
TRACE_ERROR("Protocol error. Password change", FALSE);
return FALSE;
}
/* pack local and remote paths into buffer */
strcpy(buffer,serverpath);
pstr = buffer+strlen(serverpath)+1;
strcpy(pstr,clientpath);
return ss_sendrequest( hpipe
, SSREQ_NEXTFILE
, buffer
, strlen(serverpath)+strlen(clientpath)+2
, 0
);
} /* ss_bulkcopy */
/* Remove from the decompressers list those that have finished.
Don't hang about waiting. Leave the rest on the list
Updating nGoodFiles and/or nBadFiles for those that finished.
*/
void PurgeDecomps(LIST Decs)
{
HANDLE * phThread, * Temp;
DWORD ExitCode;
phThread = List_First(Decs);
while (phThread!=NULL) {
if (GetExitCodeThread(*phThread, &ExitCode)) {
Temp = phThread;
phThread = List_Next((LPVOID)phThread);
if (ExitCode==1) {
++nGoodFiles;
CloseHandle(*Temp);
Trace_Fil("Purged a good decomp\n");
List_Delete((LPVOID)Temp);
}
else if(ExitCode==0) {
++nBadFiles;
CloseHandle(*Temp);
Trace_Fil("Purged a bad decomp\n");
List_Delete((LPVOID)Temp);
}
else /* still active? */ ;
}
} /* traverse */
} /* PurgeDecomps */
/* WAIT for each of the hThreads on Decomps and report its status by
updating nGoodFiles and/or nBadFiles
This thread must be run on the receive thread or else after
the receive thread has terminated (or else we need a critical section).
*/
void WaitForDecomps(LIST Decs)
{
HANDLE * phThread;
DWORD ExitCode;
List_TRAVERSE(Decs, phThread) {
if (bAbort) return;
Trace_Fil("Waiting for a decomp...");
for (; ; ){
DWORD rc;
rc = WaitForSingleObject(*phThread, 5000);
if (rc==0) break; /* timeout complete */
if (bAbort) {
// This WILL leave a garbage thread and a temp file lying around!!!???
Trace_Fil("Aborting wait for decomp.");
return;
}
}
Trace_Fil(" Done waiting.\n");
GetExitCodeThread(*phThread, &ExitCode);
if (ExitCode==1)
++nGoodFiles;
else
++nBadFiles;
CloseHandle(*phThread);
} /* traverse */
Trace_Fil("All decompression finished.");
List_Destroy(&Decs);
} /* WaitForDecomps */
static void Retry(LIST Rets)
{
DECOMPARGS * da;
if (List_IsEmpty(Rets)) return;
List_TRAVERSE(Rets, da) {
if (ss_copy_reliable( Server, da->Remote, da->Real, UNCName, Password))
{ /* correct the lie we told when Decompress returned FALSE */
++nGoodFiles; --nBadFiles;
}
}
List_Destroy(&Rets);
SetNames("All errors recovered");
}/* Retry */
/* end of bulk copy. Tidy everything up */
int ss_endcopy(void)
{
Trace_Fil("ss_endcopy\n");
ss_sendrequest( hpipe, SSREQ_ENDFILES, "", 1, 0);
/* wait for receiving thread to complete (could be long) */
for (; ; ){
DWORD rc;
rc = WaitForSingleObject( hThread, 5000);
if (rc==0) break; /* thread complete */
if (bAbort) {
if (hpData!=INVALID_HANDLE_VALUE){
CLOSEHANDLE(hpData);
hpData = INVALID_HANDLE_VALUE;
}
break;
}
}
// don't close the connection until we've finished, otherwise
// someone might think it's ok to reboot the server
ss_sendrequest( hpipe, SSREQ_END, "", 1, 0);
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
WaitForDecomps(Decomps);
Decomps = NULL;
SetNames(NULL);
Retry(Retries);
Retries = NULL;
BulkCopy = FALSE;
if (nBadFiles+nGoodFiles > nFiles) return -99999; /* !!? */
if (nBadFiles+nGoodFiles < nFiles) nBadFiles = nFiles-nGoodFiles;
if (nBadFiles>0)
return -nBadFiles;
else return nGoodFiles;
} /* ss_endcopy */
#if 0
/* A SSREQ_FILES has been sent and possibly one or more files,
but we have changed our minds. We try to send an Abort request.
*/
int ss_abortcopy(void)
{
Trace_Fil("ss_abortcopy\n");
ss_sendrequest( hpipe, SSREQ_ABORT, "", 1);
{ DWORD code;
TerminateThread(hThread, &code); /* storage leak */
hThread = INVALID_HANDLE_VALUE;
}
Server[0] = '\0';
CloseHandle(hpipe);
hpipe = INVALID_HANDLE_VALUE;
CLOSEHANDLE(hpData);
hpData = INVALID_HANDLE_VALUE;
if (Decomps!=NULL){
HANDLE * phThread;
List_TRAVERSE(Decomps, phThread){
DWORD code;
TerminateThread(*phThread, &code); /* storage leak */
}
}
Decomps = NULL;
BulkCopy = FALSE;
} /* ss_abortcopy */
#endif // 0
/*
* reliably copy a file (repeat (upto N times) until checksums match)
*
* returns TRUE if it succeeded or FALSE if the connection was lost
*/
BOOL
ss_copy_reliable(PSTR server, PSTR remotepath, PSTR localpath, PSTR uncname,
PSTR password)
{
ULONG sum_local, sum_remote;
int retry;
SSNEWRESP resp;
HANDLE hpCopy = INVALID_HANDLE_VALUE; /* N.B. NOT the static global pipe! */
LONG err;
FILETIME ft;
LONG sz;
DWORD attr;
Trace_Fil("ss_copy_reliable\n");
// if (BulkCopy) {
// TRACE_ERROR("Cannot do simple copy as bulk copy is in progress", FALSE);
// return FALSE;
// }
for (retry = 0; retry < 10; retry++) {
if (bAbort) return FALSE; /* abort requested from WINDIFF */
if (hpCopy!=INVALID_HANDLE_VALUE) {
CloseHandle(hpCopy);
hpCopy = INVALID_HANDLE_VALUE;
}
{ char pipename[400];
InitPipeName(pipename, server, NPNAME);
hpCopy = ConnectPipe(pipename);
}
if (hpCopy == INVALID_HANDLE_VALUE) {
/* next retry in two seconds */
Trace_Stat("connect failed - retrying");
Sleep(1000);
continue;
}
if ((uncname != NULL) && (uncname[0]!='\0')
&& (password != NULL) && (password[0]!='\0')) {
/* send the password request */
if (!ss_sendunc(hpCopy, password, uncname)) {
TRACE_ERROR("Server connection lost", FALSE);
continue;
}
/* wait for password response */
if (0>=ss_getresponse(hpCopy, &resp)) {
TRACE_ERROR("Server connection lost", FALSE);
continue;
}
if (resp.lCode != SSRESP_END) {
TRACE_ERROR("Password attempt failed", FALSE);
return(FALSE);
}
}
/* try to copy the file */
ss_copy_file(hpCopy, remotepath, localpath);
/* whether or not he thinks he failed, we should look
* to see if the file is really there.
*/
sum_local = checksum_file(localpath, &err);
if (err!=0) continue;
sum_remote = 0;
if (!ss_checksum_remote(hpCopy, remotepath, &sum_remote, &ft, &sz, &attr)) {
/* no remote checksum - better retry */
if (!TRACE_ERROR("remote checksum failed - retry?", TRUE)) {
CloseHandle(hpCopy);
return(FALSE);
}
continue;
}
if (sum_local == sum_remote) {
/* copy succeeded */
ss_terminate(hpCopy);
return(TRUE);
}
TRACE_ERROR("files different after apparently successful copy!!?", FALSE);
} /*retry loop */
/* too many retries */
CloseHandle(hpCopy);
return(FALSE);
} /* ss_copy_reliable */
/*
* copy one file using checksum server
*
* send a SSREQ_FILE for the file, and then loop reading
* blocks until we get an error (sequence count is wrong or -1),
* or the end of file (0-length block)
*
* File sent may be compressed. We write it to a temporary file, and
* then decompress it using LZCopy. This will work even if the
* file was sent uncompressed (eg because compress.exe could not be
* executed on the server).
*/
BOOL
ss_copy_file(HANDLE hpipe, PSTR remotepath, PSTR localpath)
{
HANDLE hfile;
int sequence;
ULONG size;
SSPACKET packet;
BOOL bOK;
char szTempname[MAX_PATH];
OFSTRUCT os;
int fh1, fh2;
PSSATTRIBS attribs;
Trace_Fil("ss_copy_file\n");
/* create a temporary name */
*szTempname = 0;
GetTempPath(sizeof(szTempname), szTempname);
GetTempFileName(szTempname, "ssc", 0, szTempname);
/* try to create the temp file */
hfile = CreateFile(szTempname,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hfile == INVALID_HANDLE_VALUE) {
TRACE_ERROR("ss_copy: could not create temp file", FALSE);
return(FALSE);
}
if (!ss_sendrequest(hpipe, SSREQ_FILE, remotepath, strlen(remotepath)+1, 0)){
CloseHandle(hfile);
return(FALSE);
}
for (sequence = 0; ; sequence++) {
bOK = 0 <= ss_getblock(hpipe, (PSTR) &packet, sizeof(packet));
if (!bOK || (sequence != packet.lSequence)) {
/* error or out of sequence */
TRACE_ERROR("packet error", FALSE);
CloseHandle(hfile);
DeleteFile(szTempname);
return(FALSE);
}
if (packet.ulSize == 0) {
/*
* this is the last block (end of file).
*
* the data field for this block contains a
* SSATTRIBS struct that we can use to set the
* file times and attributes (after decompression).
*/
attribs = (PSSATTRIBS) packet.Data;
break;
}
/* check the block checksums */
if ( ( SS_VERSION==0 /* which it isn't */
&& ss_checksum_block(packet.Data, packet.ulSize) != packet.ulSum
)
|| ( SS_VERSION>0 /* which it is */
&& packet.ulSum!=0
)
) {
TRACE_ERROR("packet checksum error", FALSE);
CloseHandle(hfile);
DeleteFile(szTempname);
return(FALSE);
}
bOK = WriteFile(hfile, packet.Data, packet.ulSize, &size, NULL);
if (!bOK || (size != packet.ulSize)) {
CloseHandle(hfile);
DeleteFile(szTempname);
return(FALSE);
}
}
CloseHandle(hfile);
/* decompress the file to the original name */
fh1 = LZOpenFile(szTempname, &os, OF_READ|OF_SHARE_DENY_WRITE);
if (fh1 < 0) {
TRACE_ERROR("Failed to open file for decompression", FALSE);
} else {
fh2 = LZOpenFile(localpath, &os, OF_CREATE|OF_READWRITE|OF_SHARE_DENY_NONE);
if (fh2 < 0) {
char msg[MAX_PATH+40];
wsprintf(msg, "Could not create target file %s", localpath);
if (!TRACE_ERROR(msg, TRUE)) {
bAbort = TRUE;
}
return(FALSE);
} else {
LZCopy(fh1, fh2);
LZClose(fh1);
LZClose(fh2);
}
}
DeleteFile(szTempname);
/*
* now set file attributes and times according to the attribs
* struct we received. Remember to set file times BEFORE
* setting attributes in case the attributes include read-only!
*/
hfile = CreateFile(localpath, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!SetFileTime(hfile, &attribs->ft_create,
&attribs->ft_lastaccess,
&attribs->ft_lastwrite) ) {
TRACE_ERROR("could not set file times", FALSE);
}
CloseHandle(hfile);
if (!SetFileAttributes(localpath, attribs->fileattribs)) {
TRACE_ERROR("could not set attributes", FALSE);
}
return(TRUE);
} /* ss_copy_file */
/* produce a checksum of a block of data.
*
* This algorithm is compute bound. It's probably overkill anyway and for
* version 1 is not used. It must match the one in server.
*
* 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 */
Trace_Fil("ss_checksum_block\n");
length = size;
for (Byte = 0; Byte < length ;++Byte, ++lIndex) {
lRand = lRand*lSeed;
lCheckSum += lIndex*(1+block[Byte])*lRand;
}
return(lCheckSum);
}