553 lines
18 KiB
C
553 lines
18 KiB
C
/**************************************************************************************\
|
|
* Chksum.c
|
|
* Purpose: Print to stdout checksum of all files in current directory, and optionally
|
|
* recurse from current directory.
|
|
*
|
|
* Created 02-15-95. DonBr
|
|
*
|
|
\**************************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <search.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <io.h>
|
|
#include <imagehlp.h>
|
|
#include <direct.h>
|
|
#include <ctype.h>
|
|
|
|
// type definitions
|
|
#define exename "chksum"
|
|
#define MAX_EXCLUDE (30)
|
|
#define LISTSIZE 12000 // max allowable number of files and dirs in a flat directory
|
|
|
|
typedef struct List {
|
|
char Name[MAX_PATH]; // file or directory name
|
|
unsigned long Attributes;
|
|
unsigned long Size;
|
|
} List, *pList;
|
|
|
|
// Function prototypes
|
|
VOID CheckRel();
|
|
VOID CheckSum(List *rgpList, TCHAR *x); //, TCHAR *szDirectory);
|
|
int __cdecl CompFileAndDir( const void *elem1 , const void *elem2);
|
|
int __cdecl CompName( const void *elem1 , const void *elem2);
|
|
int MyGetFullPathName( IN const CHAR *InPath, IN OUT CHAR *FullPath);
|
|
VOID CreateOutputPath(char *CurrentDir, char *NewDir);
|
|
VOID ParseArgs(int *pargc, char **argv);
|
|
VOID Usage();
|
|
|
|
// Variable declarations
|
|
|
|
BOOL fRecurse = FALSE;
|
|
BOOL fPathOverride = FALSE;
|
|
BOOL fFileOut = FALSE;
|
|
BOOL fExclude = FALSE;
|
|
BOOL fFileIn = FALSE;
|
|
int DirNum = 1,DirNameSize = 0, ProcessedFiles=0, endchar=0, ExclCounter=0;
|
|
int grc=0; // global return code
|
|
char szRootDir[MAX_PATH];
|
|
char szRootDir2[MAX_PATH];
|
|
char *szFileOut;
|
|
char szFileOutFullPath[MAX_PATH];
|
|
char *szExclude[MAX_EXCLUDE];
|
|
char *szFileIn;
|
|
CHAR szDirectory[MAX_PATH] = {"."}; // default to current directory
|
|
FILE* fout;
|
|
FILE* fin;
|
|
|
|
|
|
// Begin main program
|
|
VOID __cdecl
|
|
main(
|
|
INT argc,
|
|
LPSTR argv[]
|
|
)
|
|
|
|
{
|
|
TCHAR CWD[MAX_PATH];
|
|
HANDLE logfh;
|
|
|
|
ParseArgs(&argc, argv);
|
|
|
|
// Create File if fFileOut==TRUE
|
|
if (fFileOut) {
|
|
fout = fopen(szFileOut, "w");
|
|
if (fout == NULL) {
|
|
fprintf(stderr, "Output file %s could not be created.\n", szFileOut);
|
|
exit(1);
|
|
}
|
|
|
|
}
|
|
/*
|
|
// Open File if fFileIn==TRUE
|
|
if (fFileIn) {
|
|
fin = fopen(szFileIn, "r"); // open check file
|
|
if (fin == NULL) {
|
|
fprintf(stderr, "Check file %s could not be opened.\n", szFileIn);
|
|
exit(1);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// set root path
|
|
if (fPathOverride) {
|
|
|
|
// attempt to change directories
|
|
if (_chdir(szRootDir) == -1){
|
|
fprintf(stderr, "Path not found: %s\n", szRootDir);
|
|
Usage();
|
|
}
|
|
}else{
|
|
GetCurrentDirectory(MAX_PATH, szRootDir);
|
|
}
|
|
|
|
fprintf(fout==NULL? stdout : fout , "Processing %s\n", szRootDir);
|
|
|
|
CheckRel(); // primary worker routine
|
|
|
|
fprintf(stdout, "%d files processed in %d directories\n", ProcessedFiles, DirNum);
|
|
|
|
if (fFileOut) {
|
|
fclose(fout);
|
|
}
|
|
|
|
exit(grc);
|
|
}
|
|
|
|
/**************************************************************************************\
|
|
* Checkrel
|
|
* Purpose: Create an array of List structures containing file data for the current
|
|
* directory, sort the array alphabetically placing Files first and
|
|
* directories last, and finally process the array contents. Another instance
|
|
* of checkrel is started for directories, and checksum is called for files.
|
|
\**************************************************************************************/
|
|
|
|
VOID CheckRel()
|
|
{
|
|
HANDLE fh;
|
|
TCHAR CurrentDir[MAX_PATH] = {"\0"};
|
|
TCHAR NewDir[MAX_PATH] = {"\0"};
|
|
|
|
WIN32_FIND_DATA *pfdata;
|
|
BOOL fFilesInDir=FALSE;
|
|
BOOL fDirsFound=FALSE;
|
|
int iArrayMember=0, cNumDir=0, i=0, Length=0;
|
|
pList *rgpList = NULL; // a pointer to an array of pointers to List structures
|
|
CHAR cFileNameFullPath[MAX_PATH];
|
|
|
|
pfdata = (WIN32_FIND_DATA*)malloc(sizeof(WIN32_FIND_DATA));
|
|
if (!pfdata) {
|
|
fprintf(stderr, "Not enough memory.\n");
|
|
grc++;
|
|
return;
|
|
}
|
|
|
|
// Find the first file
|
|
fh = FindFirstFile("*.*", pfdata);
|
|
if (fh == INVALID_HANDLE_VALUE) {
|
|
fprintf(fout==NULL? stdout : fout , "\t No files found\n");
|
|
free(pfdata);
|
|
grc++;
|
|
return;
|
|
}
|
|
|
|
// Allocate an array of pointers to List structures
|
|
rgpList = (pList *) malloc(LISTSIZE * sizeof(pList));
|
|
if (!rgpList) {
|
|
fprintf(stderr, "Not enough memory allocating rgpList[].\n");
|
|
free(pfdata);
|
|
FindClose(fh); // close the file handle
|
|
grc++;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// DoWhile loop to find all files and directories in current directory
|
|
// and copy pertinent data to individual List structures.
|
|
//
|
|
do { // while (FindNextFile(fh, pfdata))
|
|
|
|
if (strcmp(pfdata->cFileName, ".") && strcmp(pfdata->cFileName, "..")) { // skip . and ..
|
|
|
|
//
|
|
// If excluding files and current file matches any excluded file
|
|
// (case insensitively), then don't process current file
|
|
//
|
|
if (fExclude) {
|
|
for (i=0; i < ExclCounter; i++) {
|
|
if (!_strcmpi(pfdata->cFileName, szExclude[i])) {
|
|
goto excludefound;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If current file matches output file name, then don't process current file
|
|
//
|
|
if ((fFileOut) && (!strcmp(szFileOut, pfdata->cFileName)) ) {
|
|
|
|
// File names match. If full paths match, ignore the output file.
|
|
|
|
MyGetFullPathName(pfdata->cFileName, cFileNameFullPath);
|
|
if ( !_strcmpi( szFileOutFullPath, cFileNameFullPath) ) {
|
|
goto excludefound;
|
|
}
|
|
}
|
|
|
|
rgpList[iArrayMember] = (pList)malloc(sizeof(List)); // allocate the memory
|
|
|
|
if (!rgpList[iArrayMember]) {
|
|
fputs("Not enough memory.\n", stderr);
|
|
free(pfdata);
|
|
FindClose(fh); // close the file handle
|
|
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
|
|
free(rgpList);
|
|
grc++;
|
|
return;
|
|
}
|
|
|
|
strcpy(rgpList[iArrayMember]->Name, pfdata->cFileName);
|
|
_strlwr(rgpList[iArrayMember]->Name); // all lowercase for strcmp in CompName
|
|
memcpy(&(rgpList[iArrayMember]->Attributes), &pfdata->dwFileAttributes, 4);
|
|
memcpy(&(rgpList[iArrayMember]->Size), &pfdata->nFileSizeLow, 4);
|
|
|
|
if (!(rgpList[iArrayMember]->Attributes & FILE_ATTRIBUTE_DIRECTORY)) { //If file
|
|
fFilesInDir=TRUE;
|
|
} else {
|
|
if (rgpList[iArrayMember]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { //If directory
|
|
fDirsFound=TRUE;
|
|
}
|
|
if (fRecurse) { // if recursive increment directory counter
|
|
cNumDir++;
|
|
}
|
|
}
|
|
|
|
iArrayMember++;
|
|
if (iArrayMember >= LISTSIZE) {
|
|
GetCurrentDirectory(MAX_PATH, CurrentDir);
|
|
fprintf(stderr, "More than %d files in %s. \nRebuild chksum.exe or eliminate some files from the root of this directory.\n", LISTSIZE, CurrentDir);
|
|
free(pfdata);
|
|
FindClose(fh); // close the file handle
|
|
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
|
|
free(rgpList);
|
|
grc++;
|
|
return;
|
|
}
|
|
excludefound: ;
|
|
}
|
|
|
|
|
|
} while (FindNextFile(fh, pfdata));
|
|
|
|
if (pfdata) free(pfdata);
|
|
if (fh) FindClose(fh); // close the file handle
|
|
|
|
//
|
|
// if no directories or files found with exception of . and ..
|
|
//
|
|
if ( (iArrayMember==0) || (!fFilesInDir) ){
|
|
|
|
GetCurrentDirectory(MAX_PATH, CurrentDir);
|
|
|
|
CreateOutputPath(CurrentDir, NewDir);
|
|
|
|
// fprintf(fout==NULL? stdout : fout , "%s - No files\n", NewDir);
|
|
}
|
|
|
|
// Sort Array arranging FILE entries at top
|
|
qsort( (void *)rgpList, iArrayMember, sizeof(List *), CompFileAndDir);
|
|
|
|
// Sort Array alphabetizing only FILE names
|
|
qsort( (void *)rgpList, iArrayMember-cNumDir, sizeof(List *), CompName);
|
|
|
|
// Sort Array alphabetizing only DIRectory names
|
|
qsort( (void *)&rgpList[iArrayMember-cNumDir], cNumDir, sizeof(List *), CompName);
|
|
|
|
//
|
|
// Process newly sorted structures.
|
|
// Checksum files or start another instance of checkrel() for directories
|
|
//
|
|
for (i=0; i < iArrayMember; ++i) {
|
|
|
|
if (rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { // if Dir
|
|
|
|
|
|
if (fRecurse) { // if recursive
|
|
|
|
if (_chdir(rgpList[i]->Name) == -1){ // cd into subdir and check for error
|
|
fprintf(stderr, "Unable to change directory: %s (error %d)\n", rgpList[i]->Name, GetLastError());
|
|
grc++;
|
|
|
|
} else {
|
|
DirNum++; // directory counter
|
|
CheckRel(); // start another iteration of checkrel function in new directory
|
|
_chdir(".."); // get back to previous directory when above iteration returns
|
|
|
|
} // end if _chdir
|
|
|
|
} // end if recurse
|
|
|
|
} else { // else if not Directory
|
|
GetCurrentDirectory(MAX_PATH, CurrentDir);
|
|
|
|
CreateOutputPath(CurrentDir, NewDir);
|
|
|
|
CheckSum(rgpList[i], NewDir);
|
|
}
|
|
|
|
} // end for i < iArrayMember
|
|
|
|
// Clean up the array and it's elements
|
|
for (i=0; i<iArrayMember; i++) free(rgpList[i]);
|
|
free(rgpList);
|
|
|
|
} // end CheckRel
|
|
|
|
/*************************************************************************************\
|
|
* CheckSum
|
|
* Purpose: uses MapFileAndCheckSum to determine file checksum and outputs data.
|
|
\*************************************************************************************/
|
|
VOID CheckSum(List *rgpList, TCHAR *x) {//TCHAR *szDirectory) {
|
|
ULONG HeaderSum, CheckSum=0, status;
|
|
|
|
if (rgpList->Size != 0) { //High != 0 || rgpList->nFileSizeLow != 0) {
|
|
status = MapFileAndCheckSum(rgpList->Name, &HeaderSum, &CheckSum);
|
|
if (status != CHECKSUM_SUCCESS) {
|
|
fprintf(fout==NULL? stdout : fout , "\nCannot open or map file: %s (error %d)\n", rgpList->Name, GetLastError());
|
|
grc++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
fprintf(fout==NULL? stdout : fout , "%s\\%s %lx\n", x, rgpList->Name, CheckSum);//szDirectory, rgpList->Name, CheckSum);
|
|
ProcessedFiles++;
|
|
|
|
} //CheckSum
|
|
|
|
|
|
/********************************************************************************************\
|
|
* CompFileAndDir
|
|
* Purpose: a comparision routine passed to QSort. It compares elem1 and elem2
|
|
* based upon their attribute, i.e., is it a file or directory.
|
|
\********************************************************************************************/
|
|
|
|
int __cdecl
|
|
CompFileAndDir( const void *elem1 , const void *elem2 )
|
|
{
|
|
pList p1, p2;
|
|
// qsort passes a void universal pointer, use a typecast (List**)
|
|
// so the compiler recognizes the data as a List structure.
|
|
// Typecast pointer-to-pointer-to-List and dereference ONCE
|
|
// leaving a pList. I don't dereference the remaining pointer
|
|
// in the p1 and p2 definitions to avoid copying the structure.
|
|
p1 = (*(List**)elem1);
|
|
p2 = (*(List**)elem2);
|
|
|
|
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return 0;
|
|
} //both dirs
|
|
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return 0;
|
|
} //both files
|
|
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return 1;
|
|
} // elem1 is dir and elem2 is file
|
|
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return -1;
|
|
} // elem1 is file and elem2 is dir
|
|
|
|
return 0; // if none of above
|
|
|
|
}
|
|
|
|
|
|
/********************************************************************************************\
|
|
* CompName is another compare routine passed to QSort that compares the two Name strings *
|
|
\********************************************************************************************/
|
|
|
|
int __cdecl
|
|
CompName( const void *elem1 , const void *elem2 )
|
|
{
|
|
return strcmp( (*(List**)elem1)->Name, (*(List**)elem2)->Name );
|
|
}
|
|
|
|
/**********************************************************************************************\
|
|
* CreateOutputPath just formats NewDir, the path prepended to filename during checksum output *
|
|
\**********************************************************************************************/
|
|
VOID CreateOutputPath(char *CurrentDir, char *NewDir)
|
|
{
|
|
strcpy(NewDir, ".");
|
|
|
|
// if rootdir ends in '\' and currentdir and szrootdir2 don't match
|
|
// handles case where /p path override arg ends in a '\' char like "/p g:\"
|
|
// files listed at the root don't need the extra '\' placed in NewDir, but
|
|
// directories at the root DO need ".\" prepended to their name
|
|
|
|
_strlwr(CurrentDir);
|
|
//fprintf(stdout, "szrootdir: %s, szrootdir2: %s, currentdir: %s\n", szRootDir, szRootDir2, CurrentDir);
|
|
|
|
if ( (szRootDir[strlen(szRootDir)-1] == '\\') && // if arg path ends in "\"
|
|
(strcmp(CurrentDir, szRootDir2)) && // if they don't match
|
|
(CurrentDir[strlen(CurrentDir)-1] != '\\') //&& // if currentdir doesn't end with "\"
|
|
//(szRootDir2[strlen(szRootDir2)-1] != ':') ){ // if arg path ends with ":"
|
|
){
|
|
strcat(NewDir, "\\");
|
|
}
|
|
|
|
if ( (CurrentDir[strlen(CurrentDir)-2] !=':') && (CurrentDir[strlen(CurrentDir)-1] !='\\') ){
|
|
strcat(NewDir, &CurrentDir[(strlen(szRootDir))] );
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
ParseArgs(int *pargc, char **argv) {
|
|
|
|
CHAR cswitch, c, *p;
|
|
int argnum = 1;
|
|
|
|
while ( argnum < *pargc ) {
|
|
_strlwr(argv[argnum]);
|
|
cswitch = *argv[argnum];
|
|
if (cswitch == '/' || cswitch == '-') {
|
|
c = *(argv[argnum]+1);
|
|
|
|
switch (c) {
|
|
|
|
case '?':
|
|
Usage();
|
|
|
|
case 'r':
|
|
fRecurse = TRUE;
|
|
break;
|
|
|
|
case 'p':
|
|
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
|
|
++argnum; // increment to next arg string
|
|
strcpy(szRootDir, argv[argnum]);
|
|
if (szRootDir == NULL) {
|
|
fprintf(stderr, "out of memory for root dir.\n");
|
|
exit(1);
|
|
}
|
|
fPathOverride = TRUE;
|
|
|
|
// Find the full path to the root
|
|
if ( !MyGetFullPathName(argv[argnum], szRootDir) ) {
|
|
fprintf(stderr, "Cannot get full path for root dir %s\n", szRootDir);
|
|
exit(1);
|
|
}
|
|
_strlwr(szRootDir);
|
|
|
|
strcpy(szRootDir2, szRootDir);
|
|
// if path given ends in a "\", remove it...
|
|
if (szRootDir2[strlen(szRootDir2)-1] == 92) szRootDir2[strlen(szRootDir2)-1] = '\0';
|
|
break;
|
|
|
|
} else {
|
|
Usage();
|
|
}
|
|
|
|
case 'o':
|
|
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
|
|
++argnum;
|
|
szFileOut = _strdup(argv[argnum]);
|
|
if (szFileOut == NULL) {
|
|
fprintf(stderr, "Out of memory for output file.\n");
|
|
exit(1);
|
|
}
|
|
fFileOut = TRUE;
|
|
|
|
// Find the full path to the output file
|
|
if ( !MyGetFullPathName(szFileOut, szFileOutFullPath) ) {
|
|
fprintf(stderr, "Cannot get full path for output file %s\n", szFileOut);
|
|
exit(1);
|
|
}
|
|
_strlwr(szFileOutFullPath); // lower case full path to output file
|
|
_strlwr(szFileOut); // lower case path to output file
|
|
break;
|
|
|
|
} else {
|
|
Usage();
|
|
}
|
|
|
|
|
|
case 'x': // check number of args given
|
|
if ( ((argnum+1) < *pargc) && (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
|
|
++argnum;
|
|
szExclude[ExclCounter] = _strdup(argv[argnum]);
|
|
if (szExclude[ExclCounter] == NULL) {
|
|
fprintf(stderr, "Out of memory for exclude name.\n");
|
|
exit(1);
|
|
}
|
|
fExclude = TRUE;
|
|
_strlwr(szExclude[ExclCounter]);
|
|
ExclCounter++;
|
|
|
|
break;
|
|
|
|
} else {
|
|
Usage();
|
|
}
|
|
|
|
/*
|
|
case 'i':
|
|
if ( (*(argv[argnum]+2) == '\0') && (*(argv[argnum+1]) != '\0') ) {
|
|
++argnum;
|
|
szFileIn = strdup(argv[argnum]);
|
|
if (szFileIn == NULL) {
|
|
fprintf(stderr, "Out of memory for input file.\n");
|
|
exit(1);
|
|
}
|
|
fFileIn = TRUE;
|
|
break;
|
|
|
|
} else {
|
|
Usage();
|
|
}
|
|
*/
|
|
default:
|
|
fprintf(stderr, "\nInvalid argument: %s\n", argv[argnum]);
|
|
Usage();
|
|
} //switch
|
|
|
|
} else {
|
|
Usage();
|
|
} // if
|
|
++argnum;
|
|
} // while
|
|
} // parseargs
|
|
|
|
|
|
LPSTR pszUsage =
|
|
"Generates a listing of each file processed and its check sum.\n\n"
|
|
"Usage: %s [/?] display this message\n"
|
|
" [/r] recursive file check\n"
|
|
" [/p pathname] root path override\n"
|
|
" [/o filename] output file name\n"
|
|
" [/x name] exclude file or directory\n\n"
|
|
"Notes: If no /p path is given, the current directory is processed.\n"
|
|
" Exclude multiple files or directories with multiple /x arguments\n"
|
|
" e.g. - /x file1 /x file2\n\n"
|
|
"Example: %s /r /p c:\\winnt351 /o %s.chk /x symbols /x dump\n"
|
|
"";
|
|
|
|
VOID
|
|
Usage()
|
|
{
|
|
fprintf(stderr, pszUsage, exename, exename, exename);
|
|
exit(1);
|
|
}
|
|
|
|
int MyGetFullPathName( IN const CHAR *InPath, IN OUT CHAR *FullPath)
|
|
{
|
|
int len;
|
|
LPSTR FilePart;
|
|
len = GetFullPathName(InPath, MAX_PATH, FullPath, &FilePart);
|
|
return ( (len>0 && len<MAX_PATH) ? len : 0 );
|
|
}
|
|
|