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

1605 lines
39 KiB
C

#ifndef WIN32
#define RC_INVOKED
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <malloc.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <conio.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <imagehlp.h>
#define READ_BUFFER_SIZE (16 * 1024 * sizeof(DWORD)) // 64k blocks
#define CHECK_NAME "\\chkfile.chk"
LPSTR
atolx(
LPSTR psz,
LPDWORD pul);
DWORD
ParseCheckFile (
VOID
);
HANDLE
PortFindFirstFile(
LPSTR FindPattern,
BOOL fNormal,
LPSTR FindName,
LPDWORD FindSize);
BOOL
PortFindNextFile(
HANDLE hFind,
BOOL fNormal,
LPSTR FindName,
LPDWORD FindSize);
VOID
PortFindClose(
HANDLE hFind
);
UINT ProcessCheckFile(LPINT pcfiles);
VOID Usage(VOID);
VOID __cdecl crerror(LPSTR pszfmt, ...);
LPSTR *ProcessParameters(INT *pargc, LPSTR argv[]);
LPSTR ProcessArgBuf(LPSTR pszargs);
BOOL OpenCheckFile(VOID);
BOOL ProcessEntry(LPSTR pszFullPath, LPSTR pszRelPath);
BOOL FindEntry(LPSTR pszRelPath, PULONG pSum);
DWORD MissingEntries(VOID);
VOID ReadCheckHeader(FILE *pf);
VOID RecursiveCheckHeader(void);
VOID WriteCheckHeader(FILE *pf);
LPSTR iscomment(LPSTR psz);
LPSTR ismatch(LPSTR psz, LPSTR pszcompact);
LPSTR iscomment(LPSTR psz);
LPSTR AddDirectory(LPSTR psz);
BOOL AddEntry(LPSTR psz, BOOL frequired);
BOOL AddComponent(LPSTR pszdir, LPSTR pszpat, BOOL fdir, BOOL frequired);
LPSTR ReadDirectory(LPSTR pszdir);
#define CHECKSTRLEN(psz, cbmax) \
if (strlen(psz) > cbmax) { \
crerror("String overflow at line %u (%s)", __LINE__, psz); \
exit(4); \
}
#define DEFAULTROOT "nt"
//
// Defined parsed check file entry structure and table storage.
//
typedef struct _CHECK_FILE_ENTRY {
struct _CHECK_FILE_ENTRY *Next;
DWORD Sum;
WORD Length;
CHAR *Name;
} CHECK_FILE_ENTRY, *PCHECK_FILE_ENTRY;
#define CHECK_ENTRY_TABLE_SIZE 4096
CHECK_FILE_ENTRY CheckEntryTable[CHECK_ENTRY_TABLE_SIZE];
//
// Define root of parsed check file list.
//
CHECK_FILE_ENTRY CheckEntryRoot;
struct component_s {
struct component_s *pcmNext; // next in linked list
BOOL fDir; // TRUE if directory
BOOL fFound; // TRUE if found
BOOL fRequired; // TRUE if must exist
CHAR achPat[1]; // path component (subdir or pattern)
};
struct checkpath_s {
struct checkpath_s *pcpNext; // next in linked list
struct component_s *pcmPat; // subdirectories and file patterns
CHAR achDir[1]; // root relative directory path
};
struct checkpath_s *pcpPaths = NULL;
DWORD cbCheck;
LPSTR pszCheckFileName = NULL; // input/output check file path
LPSTR pszLogFileName = NULL; // error log file path
FILE *pfCheck = NULL; // input/output check stdio file pointer
FILE *pfLog; // error log file pointer
LPSTR pszCheck = NULL; // input check file contents
LPSTR RootOfTree = DEFAULTROOT;
BOOL fInProgress = FALSE;
UINT cbProgress = 0;
BOOL fAll = FALSE;
BOOL fCommand = FALSE;
BOOL fGenerateCheck = FALSE;
BOOL fNoArgs = FALSE;
BOOL fRecurse = FALSE;
BOOL fPrintMissing = TRUE;
BOOL fPrintExtra = TRUE;
DWORD fCdCheck;
CHAR OutputLine[512];
DWORD ReadBuffer[READ_BUFFER_SIZE / sizeof(DWORD) + 1];
//
// this table must be in alphabetical order !!!
//
LPSTR pszDefaultDir =
"#directory start\n"
#if defined(i386)
"*.\n"
"*.com\n"
#endif
#if defined(MIPS) || defined(_ALPHA_)
"*.dll\n"
"*.exe\n"
#endif
#if defined(PPC)
"*.exe\n"
#endif
"?\\*.*\n"
"?\\dump\\ optional\n"
"?\\dump\\*.* optional\n"
"?\\idw\\ optional\n"
"?\\idw\\*.* optional\n"
"?\\idw\\setup\\ optional\n"
"?\\idw\\setup\\*.* optional\n"
"?\\km\\ optional\n"
"?\\km\\*.* optional\n"
"?\\km\\symbols\\ optional\n"
"?\\km\\symbols\\dll\\ optional\n"
"?\\km\\symbols\\dll\\*.* optional\n"
"?\\km\\symbols\\sys\\ optional\n"
"?\\km\\symbols\\sys\\*.* optional\n"
"?\\km\\system32\\ optional\n"
"?\\km\\system32\\*.* optional\n"
"?\\km\\system32\\drivers\\ optional\n"
"?\\km\\system32\\drivers\\*.* optional\n"
"?\\mstools\\ optional\n"
"?\\mstools\\*.* optional\n"
"?\\nws\\ optional\n"
"?\\nws\\*.* optional\n"
"?\\symbols\\*.* optional\n"
"?\\symbols\\acm\\*.* optional\n"
"?\\symbols\\com\\*.* optional\n"
"?\\symbols\\cpl\\*.* optional\n"
"?\\symbols\\dll\\*.* optional\n"
"?\\symbols\\drv\\*.* optional\n"
"?\\symbols\\exe\\*.* optional\n"
"?\\symbols\\scr\\*.* optional\n"
"?\\symbols\\sys\\*.* optional\n"
"?\\system\\*.*\n"
"?\\system32\\*.*\n"
"?\\system32\\config\\*.*\n"
"?\\system32\\dhcp\\*.* optional\n"
"?\\system32\\drivers\\*.*\n"
"?\\system32\\drivers\\etc\\*.*\n"
#ifdef i386
"?\\system32\\os2\\ optional\n"
"?\\system32\\os2\\dll\\ optional\n"
"?\\system32\\os2\\dll\\*.* optional\n"
#endif
"?\\system32\\ras\\*.* optional\n"
"?\\system32\\spool\\ optional\n"
"?\\system32\\spool\\drivers\\ optional\n"
"?\\system32\\spool\\prtprocs\\ optional\n"
#ifdef MIPS
"?\\system32\\spool\\prtprocs\\w32mips\\ optional\n"
"?\\system32\\spool\\prtprocs\\w32mips\\*.dll optional\n"
#endif
#ifdef _ALPHA_
"?\\system32\\spool\\prtprocs\\w32alpha\\ optional\n"
"?\\system32\\spool\\prtprocs\\w32alpha\\*.dll optional\n"
#endif
#ifdef i386
"?\\system32\\spool\\prtprocs\\w32x86\\ optional\n"
"?\\system32\\spool\\prtprocs\\w32x86\\*.dll optional\n"
#endif
#ifdef PPC
"?\\system32\\spool\\prtprocs\\w32ppc\\ optional\n"
"?\\system32\\spool\\prtprocs\\w32ppc\\*.dll optional\n"
#endif
"?\\system32\\wins\\*.* optional\n"
"?\\ui\\ optional\n"
"?\\ui\\*.* optional\n"
"?\\ui\\dump\\ optional\n"
"?\\ui\\dump\\*.* optional\n"
"?\\ui\\symbols\\ optional\n"
"?\\ui\\symbols\\cpl\\ optional\n"
"?\\ui\\symbols\\cpl\\*.* optional\n"
"?\\ui\\symbols\\dll\\ optional\n"
"?\\ui\\symbols\\dll\\*.* optional\n"
"?\\ui\\symbols\\exe\\ optional\n"
"?\\ui\\symbols\\exe\\*.* optional\n"
"?\\ui\\system32\\ optional\n"
"?\\ui\\system32\\*.* optional\n"
#ifdef i386
"?\\wdl\\ optional\n"
"?\\wdl\\video\\ optional\n"
"?\\wdl\\video\\avga\\ optional\n"
"?\\wdl\\video\\avga\\*.* optional\n"
#endif
"#directory end\n"
"";
VOID
CdCheck()
{
#if 0
LPSTR line=NULL;
LPSTR psz;
CHAR partialname[256];
CHAR fullname[256];
char flatname[256];
DWORD ChkFileSum,ChkFileSize;
LPSTR FilePart;
DWORD ActualSize, ActualSum;
FILE *pf = NULL;
//
// We are checking the CD. Read the entire checkfile
// and cross check each entry against contents of the
// CD
//
line = pszCheck;
for ( line = pszCheck; line != NULL ; line = strchr(line, '\n')) {
if (line >= pszCheck + cbCheck - 1) {
line = pszCheck;
}
if (*line == '\n') {
line++;
}
if (*line == '\0') {
break;
}
if (*line == '\n') {
continue; // skip used entries & empty lines
}
psz = line;
while (*psz == ' ' || *psz == '\t') {
psz++; // skip leading whitespace
}
if (*psz == '\n') {
continue; // skip empty line
}
//
// psz points to name sum size
//
sscanf(psz,"%s %x %x",partialname,&ChkFileSum,&ChkFileSize);
GetFullPathName(partialname,sizeof(fullname),fullname,&FilePart);
strcpy(flatname,RootOfTree);
strcat(flatname,"\\");
strcat(flatname,FilePart);
pf = fopen(flatname, "rb");
if (pf == NULL) {
strcpy(flatname,RootOfTree);
strcpy(flatname+2,"\\mstools\\bin");
strcat(flatname,RootOfTree+2);
strcat(flatname,"\\");
strcat(flatname,FilePart);
pf = fopen(flatname, "rb");
if (pf == NULL) {
if ( strstr(partialname,"idw\\") ) {
goto nextone;
}
if ( strstr(partialname,"dump\\") ) {
goto nextone;
}
crerror("Cannot open file(%d): %s", errno, FilePart);
goto nextone;
}
}
ActualSize = _filelength(_fileno(pf));
if (ActualSize == 0xffffffff) {
crerror("Cannot determine size of file: %s %d", FilePart, errno);
fclose(pf);
goto nextone;
}
if (ActualSize != ChkFileSize) {
crerror("Size differs (actual %lx, expected %lx): %s",
ActualSize,
ChkFileSize,
FilePart);
fclose(pf);
goto nextone;
}
// ActualSum = CheckSumFile(pf, flatname, flatname, &ActualSize);
if (ActualSum != ChkFileSum) {
crerror("Sum differs (actual %lx, expected %lx): %s",
ActualSum,
ChkFileSum,
FilePart);
}
nextone:;
}
#endif /* 0 */
}
INT __cdecl
main(
INT argc,
LPSTR argv[]
)
{
UINT rc;
pfLog = stderr;
//
// Initialize check file entry root list entry.
//
CheckEntryRoot.Next = NULL;
argv = ProcessParameters(&argc, argv);
if (fCommand) {
pfCheck = stdout;
rc = 0;
while (argc > 1) {
argc--;
argv++;
_strlwr(*argv);
if (!ProcessEntry(*argv, *argv)) {
rc++;
}
}
} else {
long l;
time_t t = time(NULL);
INT cfiles;
// If we are generating a check file, then generate it.
// Otherwise just check the release.
rc = ProcessCheckFile(&cfiles);
l = (long)(time(NULL) - t);
printf("\n%3u files: %lu:%02lu\n", cfiles, l/60, l % 60);
}
exit(rc);
return rc;
}
LPSTR pszUsage =
"usage: checkrel [-?] display this message\n"
" [-a] process all files\n"
" [-c] command line contains file names to sum\n"
" [-f chkfile] input/output check file override\n"
" [-g] generate check file\n"
" [-l logfile] stderr log file\n"
" [-n] suppress check file arguments\n"
" [-r pathname] root path override\n"
" [-R] recursive file check\n"
" [-m] master cdrom check\n"
" [-i] don't warn about missing files\n"
" [-x] don't warn about extra files\n"
"";
VOID
Usage(VOID)
{
fprintf(stderr, pszUsage);
exit(1);
}
VOID
__cdecl
crerror(
LPSTR pszfmt,
...
)
{
va_list argptr;
va_start(argptr, pszfmt);
if (fInProgress && pfLog == stderr) {
printf("\r%*s\r", cbProgress, ""); // clear line
fflush(stdout);
fInProgress = FALSE;
}
fprintf(pfLog, "CheckRel: ");
vfprintf(pfLog, pszfmt, argptr);
fprintf(pfLog, "\n");
}
LPSTR *
ProcessParameters(INT *pargc, LPSTR argv[])
{
CHAR cswitch, c, *p;
while (*pargc > 1) {
--(*pargc);
p = *++argv;
if ((cswitch = *p) == '/' || cswitch == '-') {
while (c = *++p) {
switch (c) {
case '?':
Usage();
case 'm': fCdCheck++; break;
case 'a': fAll++; break;
case 'c': fCommand++; break;
case 'g': fGenerateCheck++; break;
case 'n': fNoArgs++; break;
case 'i': fPrintMissing = FALSE; break;
case 'x': fPrintExtra = FALSE; break;
case 'R': fRecurse++; break;
case 'f':
if (p[1] == '\0' && --(*pargc)) {
++argv;
if (pszCheckFileName == NULL) {
pszCheckFileName = *argv;
break;
}
crerror("Check file specified twice: -f %s -f %s",
pszCheckFileName,
*argv);
Usage();
}
Usage();
case 'l':
if (p[1] == '\0' && --(*pargc)) {
++argv;
if (pszLogFileName == NULL) {
pfLog = fopen(*argv, "wt");
if (pfLog == NULL) {
pfLog = stderr;
crerror("Cannot open %s (%d)", *argv, errno);
exit(2);
}
pszLogFileName = *argv;
break;
}
crerror("Log file specified twice: -l %s -l %s",
pszLogFileName,
*argv);
Usage();
}
Usage();
case 'r':
if (p[1] == '\0' && --(*pargc)) {
++argv;
RootOfTree = _strdup(*argv);
if (RootOfTree == NULL) {
crerror("Out of memory for tree root");
exit(2);
}
break;
}
Usage();
default:
crerror("Invalid switch: -%c", c);
Usage();
}
}
} else if (fCommand) {
(*pargc)++;
argv--;
break;
} else {
crerror("Extra argument: %s", p);
Usage();
}
}
if (fCommand || fRecurse) {
fGenerateCheck = TRUE;
fAll = TRUE;
}
return(argv);
}
LPSTR
ProcessArgBuf(LPSTR pszargs)
{
UINT i;
INT argc;
LPSTR pb;
LPSTR psz;
LPSTR *ppsz;
LPSTR argv[20];
CHAR achbuf[512];
ppsz = argv;
*ppsz++ = "Check File";
psz = achbuf;
if ((pb = strchr(pszargs, '\n')) != NULL) {
pb++;
while (*pszargs == ' ' || *pszargs == '\t') {
pszargs++; // skip leading white space
}
if (*pszargs == '-') {
for (;;) {
i = strcspn(pszargs, " \t\n");
*ppsz++ = psz;
if (ppsz - argv + 1 >= sizeof(argv)/sizeof(argv[0])) {
crerror("Too many file args (%d)", ppsz - argv);
exit(2);
}
if (psz - achbuf + i + 2 >= sizeof(achbuf)) {
crerror("Too many file arg chars (%d)", sizeof(achbuf));
exit(2);
}
strncpy(psz, pszargs, i);
psz += i;
*psz++ = '\0';
if (pszargs[i] == '\n') {
break;
}
pszargs += i + 1;
while (*pszargs == ' ' || *pszargs == '\t') {
pszargs++; // skip leading white space
}
}
*ppsz = NULL;
argc = (INT)(ppsz - argv);
if (!fNoArgs) {
if (fGenerateCheck) {
printf("Check file arguments:");
for (ppsz = &argv[1]; *ppsz != NULL; ppsz++) {
printf(" %s", *ppsz);
}
printf("\n");
}
ProcessParameters(&argc, argv);
}
} else {
pb = NULL;
}
}
return(pb);
}
UINT
ProcessCheckFile(
LPINT pcfiles
)
{
HANDLE hFind;
DWORD FindSize;
UINT cbFindPattern;
LPSTR FindPattern;
LPSTR pszRelPath;
LPSTR pszFile;
struct checkpath_s *pcp;
struct component_s *pcm;
CHAR FindName[MAX_PATH];
INT i;
*pcfiles = 0;
if (!OpenCheckFile()) {
return(1);
}
cbFindPattern = MAX_PATH + strlen(".") + 1;
FindPattern = malloc(cbFindPattern + 1);
if (FindPattern == NULL) {
crerror("Process: memory allocation (%d bytes) failed",
cbFindPattern + 1);
return(1);
}
//
// Set address of relative path.
//
pszRelPath = &FindPattern[strlen(".") + 1];
i = 0;
for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
i = (i & ~31) + 32;
//
// Build the initial find pattern.
//
sprintf(FindPattern,
"%s\\%s%s",
".",
pcp->achDir,
*pcp->achDir ? "\\" : "");
CHECKSTRLEN(FindPattern, cbFindPattern);
//
// Point past directory in find pattern.
//
pszFile = &FindPattern[strlen(FindPattern)];
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
i++;
if (pcm->fDir) {
continue; // process only file patterns
}
if (!fAll && *pcm->achPat == '\0') {
continue; // skip entry if no search pattern
}
// Complete FindPattern: "c:\nt\system32\*.exe"
if (fAll)
strcpy(pszFile, "*.*");
else if (pcm->achPat)
strcpy(pszFile, pcm->achPat);
else
*pcm->achPat = '\0';
CHECKSTRLEN(FindPattern, cbFindPattern);
hFind = PortFindFirstFile(FindPattern, TRUE, FindName, &FindSize);
if (hFind == INVALID_HANDLE_VALUE) {
if (pcm->fRequired) {
crerror("Missing files: %s", pszRelPath);
}
} else {
do {
// append file name to FindPattern: "c:\nt\driver\foo.sys"
_strlwr(FindName);
strcpy(pszFile, FindName);
CHECKSTRLEN(FindPattern, cbFindPattern);
if (fAll && strcmp(FindPattern, pszCheckFileName) == 0) {
continue;
}
*pcfiles += 1;
if (!ProcessEntry(FindPattern,
pszRelPath)) {
crerror("ProcessEntry failed");
return(1);
}
} while (PortFindNextFile(hFind, TRUE, FindName, &FindSize));
PortFindClose(hFind);
}
// if ignoring the supplied extensions, skip redundant patterns
if (fAll) {
break;
}
}
strcpy(pszFile, "*.*"); // search for all directories
CHECKSTRLEN(FindPattern, cbFindPattern);
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
if (pcm->fDir) { // process only directories
pcm->fFound = FALSE;
}
}
hFind = PortFindFirstFile(FindPattern, FALSE, FindName, &FindSize);
*pszFile = '\0';
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (strcmp(FindName, ".") == 0 ||
strcmp(FindName, "..") == 0) {
continue;
}
_strlwr(FindName);
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
if (pcm->fDir && strcmp(FindName, pcm->achPat) == 0) {
pcm->fFound = TRUE;
break;
}
}
if (pcm == NULL && fPrintExtra) {
crerror("Extra directory: %s%s", pszRelPath, FindName);
}
} while (PortFindNextFile(hFind, FALSE, FindName, &FindSize));
PortFindClose(hFind);
}
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
if (pcm->fDir && !pcm->fFound && fPrintMissing) {
crerror("Missing directory: %s%s", pszRelPath, pcm->achPat);
}
}
}
if (!fGenerateCheck && MissingEntries()) {
return(1);
}
if (fInProgress) {
printf("\n");
fInProgress = FALSE;
}
return(0);
}
BOOL
OpenCheckFile(
VOID
)
{
UINT cbCheckName;
// If the check file name wasn't given, then construct it.
if (pszCheckFileName == NULL) {
cbCheckName = strlen(".") + 1 + strlen(CHECK_NAME);
pszCheckFileName = malloc(cbCheckName + 1);
if (pszCheckFileName == NULL) {
crerror("Open: Out of memory (%d bytes)", cbCheckName + 1);
exit(2);
}
sprintf(pszCheckFileName, "%s\\%s", ".", CHECK_NAME);
}
if (fRecurse) {
RecursiveCheckHeader();
} else if (fGenerateCheck) {
ReadCheckHeader(NULL);
}
pfCheck = fopen(pszCheckFileName, fGenerateCheck||fRecurse? "wt" : "rt");
if (pfCheck == NULL) {
crerror("Cannot open %s (%d)", pszCheckFileName, errno);
return(FALSE);
}
if (fGenerateCheck) {
WriteCheckHeader(pfCheck);
} else {
ReadCheckHeader(pfCheck);
if (fCdCheck) {
CdCheck();
return FALSE;
}
}
return(TRUE);
}
VOID
ReadCheckHeader(
FILE *pf
)
{
DWORD cb;
UINT cbread, cbactual;
LPSTR pb;
if (pf == NULL) {
cbCheck = strlen(pszDefaultDir) + 1;
pszCheck = pszDefaultDir;
} else {
cbCheck = _filelength(_fileno(pfCheck)) + 1;
if ((DWORD) (size_t) cbCheck != cbCheck) {
crerror("Open: check file too large (%ld bytes)", cbCheck);
exit(2);
}
pszCheck = malloc((size_t) cbCheck);
if (pszCheck == NULL) {
crerror("Open: memory allocation (%ld bytes) failed", cbCheck);
exit(2);
}
pb = pszCheck;
cb = cbCheck - 1;
while (cb) {
cbread = (cb >= READ_BUFFER_SIZE)? READ_BUFFER_SIZE : (UINT) cb;
cbactual = fread(pb, 1, cbread, pfCheck);
if (cbread > cbactual) {
cb -= cbread - cbactual;
cbCheck -= cbread - cbactual;
}
pb += cbactual;
cb -= cbactual;
}
*pb = '\0';
}
while ((pb = iscomment(pszCheck)) != NULL ||
(pb = ProcessArgBuf(pszCheck)) != NULL) {
pszCheck = pb; // skip comment or parm line
}
if ((pb = ReadDirectory(pszCheck)) != NULL) {
pszCheck = pb; // skip directory lines
} else if (ReadDirectory(pszDefaultDir) == NULL) {
crerror("Bad internal data structure directory format");
exit(1);
}
}
LPSTR
ReadDirectory(
LPSTR pszdir
)
{
LPSTR pb;
if ((pb = ismatch(pszdir, "#directorystart")) == NULL) {
return(NULL);
}
pszdir = pb; // skip "start" line
while ((pb = ismatch(pszdir, "#directoryend")) == NULL) {
if ((pb = iscomment(pszdir)) == NULL &&
(pb = AddDirectory(pszdir)) == NULL) {
return(NULL);
}
pszdir = pb;
}
return(pb);
}
LPSTR
iscomment(
LPSTR psz
)
{
while (*psz == ' ' || *psz == '\t') {
psz++;
}
if (*psz == '\n' || *psz == '/' && psz[1] == '/') {
psz += strcspn(psz, "\n");
if (*psz == '\n') {
psz++;
}
return(psz); // return start of next line
}
return(NULL); // not a comment
}
LPSTR
ismatch(
LPSTR psz,
LPSTR pszcompact
)
{
while (*psz) {
if (*psz == ' ' || *psz == '\t') {
psz++;
continue;
}
if (*psz != *pszcompact) {
break;
}
psz++;
pszcompact++;
}
if (*psz != '\n' || *pszcompact != '\0') {
return(NULL);
}
return(psz + 1);
}
LPSTR
AddDirectory(
LPSTR psz
)
{
LPSTR pb;
BOOL frequired;
INT i, ch;
if ((pb = strchr(psz, '\n')) == NULL) {
crerror("Directory data error");
return(NULL);
}
while (*psz == ' ' || *psz == '\t') {
psz++;
}
frequired = TRUE;
i = strcspn(psz, " \t\n");
ch = psz[i];
psz[i] = '\0';
if (ch != '\n') {
frequired = !ismatch(psz + i + 1, "optional");
}
if (!AddEntry(psz, frequired)) {
psz[i] = (char)ch;
return(NULL);
}
return(pb + 1);
}
BOOL
AddEntry(LPSTR psz,
BOOL frequired
)
{
BOOL f, fdir, freq1;
INT i;
CHAR chsep;
CHAR achdir[MAX_PATH];
CHAR FullPath[MAX_PATH];
//
// If the leading character is ?, then prepend the name of the NT tree
// to the directory name.
//
if (*psz == '?') {
strcpy(&FullPath[0], RootOfTree);
strcat(&FullPath[0], psz + 1);
psz = &FullPath[0];
}
achdir[0] = '\0';
do {
i = strcspn(psz, "\\");
chsep = psz[i];
psz[i] = '\0';
fdir = freq1 = TRUE;
if (chsep == '\0' || psz[i + 1] == '\0') {
if (chsep == '\0') {
fdir = FALSE; // at end & no trailing pathsep
}
freq1 = frequired; // at end.
}
f = AddComponent(achdir, psz, fdir, freq1);
if (achdir[0] != '\0') {
strcat(achdir, "\\");
}
strcat(achdir, psz);
psz[i] = chsep;
if (!f) {
return(FALSE);
}
psz += i + 1;
} while(chsep != '\0' && *psz != '\0');
return(TRUE);
}
//struct component_s {
// struct component_s *pcmNext; // next in linked list
// BOOL fDir; // TRUE if directory
// BOOL fRequired; // TRUE if must exist
// CHAR achPat[1]; // path component (subdir or pattern)
//};
//
//struct checkpath_s {
// struct checkpath_s *pcpNext; // next in linked list
// struct component_s *pcmPat; // subdirectories and file patterns
// CHAR achDir[1]; // root relative directory path
//};
BOOL
AddComponent(
LPSTR pszdir,
LPSTR pszpat,
BOOL fdir,
BOOL frequired
)
{
struct checkpath_s *pcp;
struct checkpath_s *pcplast;
struct component_s *pcm;
struct component_s *pcmlast;
INT r;
INT t = 0;
pcplast = NULL;
for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
pcplast = pcp;
if ((r = strcmp(pszdir, pcp->achDir)) <= 0) {
break;
}
}
if (pcp == NULL || r) {
pcp = malloc(sizeof(*pcp) + strlen(pszdir));
if (pcp == NULL) {
crerror("AddComponent: out of memory");
exit(2);
}
if (pcplast == NULL) {
t |= 1;
pcp->pcpNext = NULL;
pcpPaths = pcp;
} else {
t |= 2;
pcp->pcpNext = pcplast->pcpNext;
pcplast->pcpNext = pcp;
}
pcp->pcmPat = NULL;
strcpy(pcp->achDir, pszdir);
}
pcmlast = NULL;
if (pszpat != NULL) {
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
pcmlast = pcm;
if ((r = strcmp(pszpat, pcm->achPat)) <= 0) {
break;
}
}
}
if (pcm == NULL || r) {
if (pszpat != NULL)
pcm = malloc(sizeof(*pcm) + strlen(pszpat));
else
pcm = malloc(sizeof(*pcm));
if (pcm == NULL) {
crerror("AddComponent: out of memory");
exit(2);
}
if (pcmlast == NULL) {
t |= 4;
pcm->pcmNext = NULL;
pcp->pcmPat = pcm;
} else {
t |= 8;
pcm->pcmNext = pcmlast->pcmNext;
pcmlast->pcmNext = pcm;
}
pcm->fDir = fdir;
pcm->fFound = FALSE;
pcm->fRequired = frequired;
if (pszpat == NULL)
*pcm->achPat = '\000';
else
strcpy(pcm->achPat, pszpat);
}
if (!frequired) {
pcm->fRequired = frequired;
}
return(TRUE);
}
VOID
WriteCheckHeader(FILE *pf)
{
struct checkpath_s *pcp;
struct component_s *pcm;
INT ccol;
CHAR achpath[MAX_PATH];
CHAR *psz;
CHAR SavedChar;
if (fAll) {
fprintf(pf,
"-%s\n\n",
fAll? "a" : "");
}
fprintf(pf, "#directory start\n");
for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
sprintf(achpath,
"%s%s%s%s",
pcp->achDir,
*pcp->achDir? "\\" : "",
pcm->achPat,
pcm->fDir? "\\" : "");
psz = strchr(achpath, '\\');
if (psz == NULL) {
fprintf(pf, achpath);
} else {
psz -= 1;
SavedChar = *psz;
*psz = '?';
fprintf(pf, psz);
*psz = SavedChar;
}
if (!pcm->fRequired) {
ccol = strlen(achpath);
fprintf(pf, " optional");
}
fprintf(pf, "\n");
}
}
fprintf(pf, "#directory end\n\n");
}
BOOL
ProcessEntry(
LPSTR pszFullPath,
LPSTR pszRelPath
)
{
ULONG CheckSum;
ULONG HeaderSum;
ULONG FileSum;
FILE *pf = NULL;
UINT cbLine;
CHAR *psz;
ULONG Status;
if (!fGenerateCheck) {
if (!FindEntry(pszRelPath, &FileSum)) {
if (fPrintExtra) {
crerror("Extra file: %s", pszRelPath);
}
return TRUE;
}
}
//
// Compute checksum of file.
//
Status = MapFileAndCheckSum(pszFullPath, &HeaderSum, &CheckSum);
if (Status != CHECKSUM_SUCCESS) {
crerror("Cannot open or map file %s", pszFullPath);
return TRUE;
}
if (fGenerateCheck) {
cbLine = sprintf(OutputLine,
"%s %lx\n",
pszRelPath,
CheckSum);
CHECKSTRLEN(OutputLine, sizeof(OutputLine));
psz = strchr(OutputLine, '\\');
if (fCommand || psz == NULL) {
fwrite(OutputLine, 1, cbLine, pfCheck);
} else {
psz -= 1;
*psz = '?';
fwrite(psz, 1, (size_t)(cbLine - (psz - OutputLine)), pfCheck);
}
}
if (!fGenerateCheck) {
if (CheckSum != FileSum) {
crerror("Sum differs (actual %lx, expected %lx): %s",
CheckSum,
FileSum,
pszRelPath);
}
}
return TRUE;
}
BOOL
FindEntry(
LPSTR pszRelPath,
PULONG FileSum
)
{
PCHECK_FILE_ENTRY LastEntry;
WORD Length;
PCHECK_FILE_ENTRY NextEntry;
//
// If this is the first trip through this code, then reset to the
// beginning of the check file.
//
if (CheckEntryRoot.Next == NULL) {
if (ParseCheckFile() == 0) {
return FALSE;
}
}
//
// Compute the length of the specified file name and loop through
// check file list for a matching entry.
//
Length = (WORD)strlen(pszRelPath);
LastEntry = &CheckEntryRoot;
NextEntry = LastEntry->Next;
do {
//
// If the length and the file name match, then remove the entry from
// the list and return the file size and check sum value.
//
if (NextEntry->Length == Length) {
if (strncmp(pszRelPath, NextEntry->Name, Length) == 0) {
LastEntry->Next = NextEntry->Next;
*FileSum = NextEntry->Sum;
return TRUE;
}
}
LastEntry = NextEntry;
NextEntry = NextEntry->Next;
} while (NextEntry != NULL);
//
// The specified file is not in the check file.
//
return FALSE;
}
DWORD
MissingEntries(
VOID
)
{
DWORD Count = 0;
PCHECK_FILE_ENTRY NextEntry;
//
// Scan through the check file list and display an error message for
// each missing file.
//
if (fPrintMissing) {
NextEntry = CheckEntryRoot.Next;
while (NextEntry != NULL) {
crerror("Missing file: %s", NextEntry->Name);
Count += 1;
NextEntry = NextEntry->Next;
}
}
return Count;
}
DWORD
ParseCheckFile(
VOID
)
{
DWORD Count = 0;
LPSTR pszline;
LPSTR psz;
PCHECK_FILE_ENTRY LastEntry;
WORD Length;
PCHECK_FILE_ENTRY NextEntry;
WORD SizeOfRoot;
DWORD Sum;
//
// If the check file contains no entries, then return.
//
if (*pszCheck != '\n') {
return Count;
}
//
// Scan through the check file and parse each file name, checksum, and
// size field.
//
SizeOfRoot = (WORD)strlen(RootOfTree);
LastEntry = &CheckEntryRoot;
for (pszline = pszCheck; pszline != NULL; pszline = strchr(pszline, '\n')) {
//
// Skip over the new line and search for the blank separator between
// the file name and the checksum.
//
pszline += 1;
psz = strchr(pszline, ' ');
//
// If there is no blank separator, then the end of the check file has
// been reached.
//
if (psz == NULL) {
return Count;
}
//
// Compute the length and checksum of the file entry.
//
Length = (short)(psz - pszline);
psz = atolx(psz + 1, &Sum);
//
// Allocate a check file entry for the specified file and insert it
// at the end of the check file entry list.
//
Count += 1;
if (Count > CHECK_ENTRY_TABLE_SIZE) {
crerror("Checkrel: Check Entry Table Overflow");
return 0;
}
NextEntry = &CheckEntryTable[Count - 1];
NextEntry->Next = NULL;
NextEntry->Sum = Sum;
//
// Form the file name from the NT root name and the specified path.
//
pszline[Length] = '\0';
if (*pszline == '?') {
pszline += 1;
NextEntry->Name = (CHAR *)malloc(SizeOfRoot + Length);
if (NextEntry->Name == NULL) {
crerror("Checkrel: failure to allocate check file entry");
return Count;
}
strcpy(NextEntry->Name, RootOfTree);
strcat(NextEntry->Name, pszline);
Length += SizeOfRoot - 1;
} else {
NextEntry->Name = pszline;
}
NextEntry->Length = Length;
LastEntry->Next = NextEntry;
LastEntry = NextEntry;
pszline = psz;
}
return Count;
}
LPSTR
atolx(
LPSTR psz,
LPDWORD pul)
{
DWORD ul;
char ch;
ul = 0;
while (isxdigit(*psz)) {
ch = *psz++;
if (isdigit(ch)) {
ch += 0 - '0';
} else if (islower(ch)) {
ch += 10 - 'a';
} else {
ch += 10 - 'A';
}
ul = (ul << 4) + ch;
}
*pul = ul;
return(psz);
}
VOID
RecursiveCheckHeader()
{
HANDLE hFind;
DWORD FindSize;
UINT cbFindPattern;
LPSTR FindPattern;
LPSTR pszRelPath;
LPSTR pszFile;
struct checkpath_s *pcp;
struct component_s *pcm;
CHAR FindName[MAX_PATH];
INT i;
cbFindPattern = strlen(".") + MAX_PATH;
FindPattern = malloc(cbFindPattern + 1);
if (FindPattern == NULL) {
crerror("Process: memory allocation (%d bytes) failed",
cbFindPattern + 1);
return;
}
// Set relative path pointer into FindPattern: "driver\elnkii.sys"
pszRelPath = &FindPattern[strlen(RootOfTree) + 1];
AddComponent(".", NULL, TRUE, TRUE);
i = 0;
for (pcp = pcpPaths; pcp != NULL; pcp = pcp->pcpNext) {
i = (i & ~31) + 32;
// Build Initial FindPattern directory path: "c:\nt\"
sprintf(FindPattern,
"%s\\%s%s",
".",
pcp->achDir,
*pcp->achDir? "\\" : "");
CHECKSTRLEN(FindPattern, cbFindPattern);
// point past directory in FindPattern: "c:\nt\system32\"
pszFile = &FindPattern[strlen(FindPattern)];
strcpy(pszFile, "*.*"); // search for all directories
CHECKSTRLEN(FindPattern, cbFindPattern);
hFind = PortFindFirstFile(FindPattern, FALSE, FindName, &FindSize);
*pszFile = '\0';
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (strcmp(FindName, ".") == 0 ||
strcmp(FindName, "..") == 0) {
continue;
}
_strlwr(FindName);
for (pcm = pcp->pcmPat; pcm != NULL; pcm = pcm->pcmNext) {
if (pcm->fDir && strcmp(FindName, pcm->achPat) == 0) {
pcm->fFound = TRUE;
break;
}
}
if (pcm == NULL) {
AddComponent(FindName, NULL, TRUE, TRUE);
}
} while (PortFindNextFile(hFind, FALSE, FindName, &FindSize));
PortFindClose(hFind);
}
}
if (fInProgress) {
printf("\n");
fInProgress = FALSE;
}
return;
}
#define ATTRMATCH(fnormal, attr) \
(!fNormal ^ ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
HANDLE
PortFindFirstFile(LPSTR FindPattern,
BOOL fNormal,
LPSTR FindName,
LPDWORD FindSize)
{
HANDLE hFind;
WIN32_FIND_DATA wfd;
hFind = FindFirstFile(FindPattern, &wfd);
if (hFind != INVALID_HANDLE_VALUE) {
if (!ATTRMATCH(fNormal, wfd.dwFileAttributes)) {
if (!PortFindNextFile(hFind,
fNormal,
FindName,
FindSize)) {
FindClose(hFind);
return(INVALID_HANDLE_VALUE);
}
} else {
strcpy(FindName, wfd.cFileName);
*FindSize = wfd.nFileSizeLow;
}
}
return(hFind);
}
BOOL
PortFindNextFile(HANDLE hFind,
BOOL fNormal,
LPSTR FindName,
LPDWORD FindSize)
{
BOOL b;
WIN32_FIND_DATA wfd;
do {
b = FindNextFile(hFind, &wfd);
} while (b && !ATTRMATCH(fNormal, wfd.dwFileAttributes));
if (b) {
strcpy(FindName, wfd.cFileName);
*FindSize = wfd.nFileSizeLow;
}
return(b);
}
VOID
PortFindClose(HANDLE hFind)
{
FindClose(hFind);
}