|
|
/*
* 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];
/* 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 */
|