252 lines
6.4 KiB
C
252 lines
6.4 KiB
C
|
/*
|
||
|
* 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 */
|