/* * 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 #include #include #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 */