_FILE VERIFICATION USING CRC_ by Mark R. Nelson [LISTING ONE] /************************** Start of CRCMAN.C ************************* * This program is used to build a list of CRC-32 values for all of files in a * given directory tree. After building file, program can be run later to * verify CRC values, giving assurance of integrity of files. To build CRC file * command line is: CRCMAN -b root-dir crc-file-name * To check list of files created, run with: CRCMAN crc-file-name * Should work with most 16 and 32-bit compilers under MS-DOS and UNIX. */ #include #include #include #include #include unsigned long CRCTable[ 256 ]; /* To build this program under UNIX, define UNIX either on the command line or * by editing this file. To define it on the command line, program should be * built like this: cc -o crcman -DUNIX crcman.c Code in this program assumes * UNIX compiler is K&R variety; does away with real function prototyping. */ #ifdef UNIX #include #ifdef M_XENIX #include #else #include #endif /* M_XENIX */ #define SEPARATOR "/" #define FILENAME_SIZE 81 void FatalError(); unsigned long CalculateFileCRC(); void ProcessAllFiles(); void BuildCRCFile(); void CheckFiles(); unsigned long CalculateBufferCRC(); void BuildCRCTable(); #else /* not UNIX, must be MSDOS */ /* Most MS-DOS compilers have converged on same names for structures and * functions used when searching directories. Borland C implementations use a * variant, requiring macro definitions. Functions work in an identical manner, * so actual implementation of code is straightforward. Addition of MS-DOS * definition helps convince Zortech compiler to use same structure and * function names as everyone else. */ #define MSDOS 1 #include #include #define SEPARATOR "\\" #define FILENAME_SIZE FILENAME_MAX #ifdef __TURBOC__ #include #define FILE_INFO struct ffblk #define FIND_FIRST( n, i ) findfirst( ( n ), ( i ), FA_DIREC ) #define FIND_NEXT( info ) findnext( ( info ) ) #define FILE_NAME( info ) ( ( info ).ff_name ) #else #define FILE_INFO struct find_t #define FIND_FIRST( n, i ) _dos_findfirst( (n), _A_SUBDIR, (i) ) #define FIND_NEXT( info ) _dos_findnext( ( info ) ) #define FILE_NAME( info ) ( ( info ).name ) #endif void FatalError( char *fmt, ... ); unsigned long CalculateFileCRC( FILE *file ); void ProcessAllFiles( char *path, FILE *crc_file ); void BuildCRCFile( char *input_dir_name, char *crc_file_name ); void CheckFiles( char *crc_file_name ); unsigned long CalculateBufferCRC( unsigned int count, unsigned long crc, void *buffer ); void BuildCRCTable( void ); #endif /* UNIX */ /* Main program checks for valid occurences of two different types of command * lines, and executes them if found. Otherwise, it prints out a simple * usage statement and exits. */ int main( argc, argv ) int argc; char *argv[]; { setbuf( stdout, NULL ); BuildCRCTable(); if ( argc == 2 ) CheckFiles( argv[ 1 ] ); else if ( argc == 4 && strcmp( argv[ 1 ], "-b" ) == 0 ) BuildCRCFile( argv[ 2 ], argv[ 3 ] ); else { printf( "Usage: CRCMAN [-b input_dir] crc-file \n" ); printf( "\n" ); printf( "Using the -b option checks all files under the input_dir\n" ); printf( "and appends their data to the crc-file. Otherwise, the\n" ); printf( "program checks the CRC data of all of the files in the\n" ); printf( "crc-file and prints the results\n" ); return( 1 ); } return( 0 ); } /* Instead of performing a straightforward calculation of the 32-bit CRC using * a series of logical operations, program uses faster table lookup method. */ #define CRC32_POLYNOMIAL 0xEDB88320L void BuildCRCTable() { int i; int j; unsigned long crc; for ( i = 0; i <= 255 ; i++ ) { crc = i; for ( j = 8 ; j > 0; j-- ) { if ( crc & 1 ) crc = ( crc >> 1 ) ^ CRC32_POLYNOMIAL; else crc >>= 1; } CRCTable[ i ] = crc; } } /* Routine checks CRC values for a list of files. */ void CheckFiles( crc_file_name ) char *crc_file_name; { FILE *crc_file; FILE *test_file; unsigned long log_crc; unsigned long crc; char log_name[ FILENAME_SIZE ]; int result; crc_file = fopen( crc_file_name, "r" ); if ( crc_file == NULL ) FatalError( "Couldn't open the log file: %s\n", crc_file_name ); for ( ; ; ) { result = fscanf( crc_file, "%lx %s", &log_crc, log_name ); if ( result < 2 ) break; test_file = fopen( log_name, "rb" ); if ( test_file != NULL ) { printf( "Checking %s ", log_name ); crc = CalculateFileCRC( test_file ); fclose( test_file ); if ( crc != log_crc ) printf( "Error: Expected %08lx, got %08lx\n", log_name, log_crc, crc ); else printf( "OK\n" ); } else printf( "Could not open file %s\n", log_name ); } } /* ProcessAllFiles() scans directory. Routine also makes sure that directory * name passed on command line is stripped of trailing '/' or '\' character. */ void BuildCRCFile( input_dir_name, crc_file_name ) char *input_dir_name; char *crc_file_name; { char path[ FILENAME_SIZE ]; FILE *crc_file; strcpy( path, input_dir_name ); if ( path[ strlen( path ) - 1 ] == SEPARATOR[ 0 ] ) path[ strlen( path ) - 1 ] = '\0'; crc_file = fopen( crc_file_name, "w" ); if ( crc_file == NULL ) FatalError( "Can't open crc log file: %s\n", crc_file_name ); ProcessAllFiles( path, crc_file ); } /* This routine is responsible for actually performing the calculation of the * 32-bit CRC for the entire file. We precondition the CRC value with all 1's, * then invert every bit after the entire file has been done. This gives us a * CRC value that corresponds with the values calculated by PKZIP and ARJ. */ unsigned long CalculateFileCRC( file ) FILE *file; { unsigned long crc; int count; unsigned char buffer[ 512 ]; int i; crc = 0xFFFFFFFFL; i = 0; for ( ; ; ) { count = fread( buffer, 1, 512, file ); if ( ( i++ % 32 ) == 0 ) putc( '.', stdout ); if ( count == 0 ) break; crc = CalculateBufferCRC( count, crc, buffer ); } putc( ' ', stdout ); return( crc ^= 0xFFFFFFFFL ); } /* This is the routine that is responsible for calculating all of CRC values * for files in a given directory. The CRC values and file names are written * out to the crc_file. */ void ProcessAllFiles( path, crc_file ) char *path; FILE *crc_file; { #ifdef UNIX DIR *dirp; #ifdef M_XENIX struct direct *entry; #else struct dirent *entry; #endif /* M_XENIX */ #define NAME entry->d_name #else FILE_INFO fileinfo; int done; #define NAME FILE_NAME( fileinfo ) #endif char fullname[ FILENAME_SIZE ]; struct stat buf; unsigned long crc; FILE *file; printf( "Searching %s\n", path ); strcat( path, SEPARATOR ); #ifdef UNIX dirp = opendir( path ); if ( dirp == NULL ) FatalError( "Error opening directory %s\n", path ); entry = readdir( dirp ); while ( entry != 0 ) { #else strcpy( fullname, path ); strcat( fullname, "*.*" ); done = FIND_FIRST( fullname, &fileinfo ); while ( done == 0 ) { #endif strcpy( fullname, path ); if ( strcmp( NAME, "." ) && strcmp( NAME, ".." ) ) { strcat( fullname, NAME ); if ( stat( fullname, &buf ) == -1 ) FatalError( "Error reading stat from file %s!\n", fullname ); if ( buf.st_mode & S_IFDIR ) ProcessAllFiles( fullname, crc_file ); else { file = fopen( fullname, "rb" ); if ( file != NULL ) { printf( "Scanning %s ", fullname ); crc = CalculateFileCRC( file ); putc( '\n', stdout ); fprintf( crc_file, "%08lx %s\n", crc, fullname ); fclose( file ); } else printf( "Could not open %s!\n", fullname ); } } #ifdef UNIX entry = readdir( dirp ); #else done = FIND_NEXT( &fileinfo ); #endif } } /* Routine calculates the CRC for a block of data using table lookup method. * It accepts an original value for the crc, and returns the updated value. */ unsigned long CalculateBufferCRC( count, crc, buffer ) unsigned int count; unsigned long crc; void *buffer; { unsigned char *p; unsigned long temp1; unsigned long temp2; p = (unsigned char*) buffer; while ( count-- != 0 ) { temp1 = ( crc >> 8 ) & 0x00FFFFFFL; temp2 = CRCTable[ ( (int) crc ^ *p++ ) & 0xff ]; crc = temp1 ^ temp2; } return( crc ); } /* Fatal error handler prints a formatted error message and then exits. */ #ifdef UNIX void FatalError( va_alist ) va_dcl { char *fmt; va_list argptr; va_start( argptr ); fmt = va_arg( argptr, char * ); #else void FatalError( char *fmt, ... ) { va_list argptr; va_start( argptr, fmt ); #endif printf( "Fatal error: " ); vprintf( fmt, argptr ); va_end( argptr ); exit( -1 ); }