home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / trojan_p / filetest.arc / FILEREAD.PAS < prev    next >
Pascal/Delphi Source File  |  1988-03-13  |  17KB  |  571 lines

  1. {  PROGRAM TO CREATE OF FILE OF THE CRC'S OF THE FILES ON THE DEFAULT DISK  }
  2.  
  3. {
  4.  
  5.   This program was written by Ted H. Emigh, and has been placed in the public
  6.   domain, to be used at the user's discretion.  The CRC routines and the
  7.   discussion of the CRC were written by David Dantowitz, Digital Equipment
  8.   Corporation,  Dantowitz%eagle1.dec@decwrl.
  9.  
  10.   This program calculates the CRC (cyclic redundancy check) for all the files
  11.   on the disk.  The CRC's are placed in a file (FILETEST.NEW) to be compared
  12.   with the CRC's calculated at a previous time in the file FILETEST.OLD.
  13.   The comparison is done with the program FILECOMP.PAS.
  14.  
  15.    For a good discussion of polynomial selection see "Cyclic
  16.    Codes for Error Detection", by W. W. Peterson and
  17.    D. T. Brown, Proceedings of the IEEE, volume 49, pp 228-235,
  18.    January 1961.
  19.  
  20.    A reference on table driven CRC computation is "A Cyclic
  21.    Redundancy Checking (CRC) Algorithm" by A. B. Marton and
  22.    T. K. Frambs, The Honeywell Computer Journal, volume 5,
  23.    number 3, 1971.
  24.  
  25.    Also used to prepare these examples was "Computer Networks",
  26.    by Andrew S. Tanenbaum, Prentice Hall, Inc.  Englewood Cliffs,
  27.    New Jersey, 1981.
  28.  
  29.    The following three polynomials are international standards:
  30.  
  31.  
  32.         CRC-12 = X^12 + X^11 + X^3 + X^2 + X^1 + 1
  33.         CRC-16 = X^16 + X^15 + X^2 + 1
  34.         CRC-CCITT = X^16 + X^12 + X^5 + 1
  35.  
  36.    The polynomials can be represented by a binary number, where a 1
  37.    indicates the inclusion of the power term in the polynomial.  Since
  38.    the highest order term is always included, that term is not needed
  39.    in specifying the polynomial, and usually is dropped.  In addition,
  40.    the bits are specified from low-order to high-order.  For example,
  41.    the polynomial CRC-12 can be represented in the following manner:
  42.  
  43.     Order                   0  1  2  3  4  5  6  7  8  9 10 11 12
  44.     Term Included ?         Y  Y  Y  Y  N  N  N  N  N  N  N  Y  Y
  45.     Binary Representation   1  1  1  1  0  0  0  0  0  0  0  1 (1)<-- DROPPED
  46.  
  47.    The binary and hex representations for the three polynomials are:
  48.  
  49.                    Binary                     Hex
  50.  
  51.         CRC-12    = 1111 0000 0001           $0F01
  52.         CRC-16    = 1010 0000 0000 0001      $A001
  53.         CRC-CCITT = 1000 0100 0000 1000      $8404    (Used below)
  54.  
  55.    The first is used with 6-bit characters and the second two
  56.    with 8-bit characters.  All of the above will detect any
  57.    odd number of errors.  The second two will catch all 16-bit
  58.    bursts, a high percentage of random 17-bit bursts (~99.997%) and
  59.    also a large percentage of random 18-bit or larger bursts (~99.998%).
  60.    The paper mentioned above (Peterson and Brown) discusses how
  61.    to compute the statistics presented which have been quoted
  62.    from Tanenbaum.  Notice that some errors can be generated in
  63.    nonrandom ways that can substantially reduce the chances of
  64.    detecting errors.
  65.  
  66.    (A burst of length N is defined a sequence of N bits, where
  67.    the first and last bits are incorrect and the bits in the
  68.    middle are any possible combination of correct and incorrect.
  69.    See the paper by Peterson and Brown for more information)
  70.  
  71. }
  72.  
  73. {$G512,P512,U+,R+ }
  74. Program FILEREAD;
  75.  
  76. Const
  77.   BufSize = 192;  { Number of 128 byte sectors in the CRC buffer }
  78.   Buffer_Length = 24576;  { BufSize * 128 = Length of the CRC buffer }
  79.   Version = 3.00;
  80.   Version_Date = '03/11/88';
  81.   POLY = $8404;  {  CRC Polynomial Used  }
  82.  
  83. label
  84.   allover;
  85. Type
  86.   Bytes = Array [1..24576] of Byte;  {  Length is 1..Buffer_Length  }
  87.  
  88.   Registers = record  {  Registers for 8088/8086/80286  }
  89.                 ax, bx, cx, dx, bp, si, di, ds, es, flags : integer;
  90.               end;
  91.  
  92.   DTA_record = record  {  DTA as used by MSDOS  }
  93.                  dos : array [1..21] of char;
  94.                  attribute : byte;  {  Attribute byte  }
  95.                  time_of_day : integer;  {  Time of Day of File Creation  }
  96.                  date : integer;  {  Date of File Creation  }
  97.                  low_size, high_size : integer;  {  Size of the File  }
  98.                  filename: array [1..13] of char;  { File Name  }
  99.                  junk : array [1..85] of byte;
  100.                end;
  101.  
  102.   string255 = string[255];
  103.  
  104. Var
  105.   {  Variables used in Calculating the CRC  }
  106.  
  107.   str_length, RecsRead, CRC_value : integer;
  108.   table_256 : Array [0 .. 255] of Integer;  {CRC Table to speed computations}
  109.   byte_string : Bytes;
  110.  
  111.   {  Variables used in setting up the input and output files  }
  112.  
  113.   filvar : file;
  114.   test_file : text;
  115.   test_total,i: integer;
  116.   doit,all_done,diagnostics: boolean;
  117.   test_files: array [1 .. 100] of string255;
  118.   test_done: array [1 .. 100] of boolean;
  119.   outfile : TEXT;
  120.   check_crc : boolean;
  121.  
  122.   {  Misc. Variables  }
  123.  
  124.   root : string255;  {  Contains the default drive and root directory }
  125.   global_reg : registers;  {  Registers for the DOS calls  }
  126.  
  127.  
  128. Procedure generate_table_256(POLY : Integer);
  129.  
  130. {
  131.     This routine computes the remainder values of 0 through 255 divided
  132.   by the polynomial represented by POLY.  These values are placed in a
  133.   table and used to compute the CRC of a block of data efficiently.
  134.   More space is used, but the CRC computation will be faster.
  135.  
  136.  
  137.  
  138.     This implementation only permits polynomials up to degree 16.
  139. }
  140.  
  141.  
  142. Var
  143.    val, i, result : Integer;
  144.  
  145. Begin
  146. For val := 0 to 255 Do
  147.   Begin
  148.      result := val;
  149.      For i := 1 to 8 Do
  150.         Begin
  151.            If (result and 1) = 1
  152.               then result := (result shr 1) xor POLY
  153.               else result :=  result shr 1;
  154.         End;
  155.  
  156.      table_256[val] := result;
  157.   End
  158. End;
  159.  
  160.  
  161. Function crc_string_256(Var s : Bytes; s_length, initial_crc : Integer)
  162.                         : Integer;
  163.  
  164. {
  165.      This routine computes the CRC value and returns it as the function
  166.   value.  The routine takes an array of Bytes, a length and an initial
  167.   value for the CRC.  The routine requires that a table of 256 values
  168.   be set up by a previous call to Generate_table_256.
  169.  
  170.       This routine uses table_256.
  171. }
  172.  
  173. Begin
  174.  
  175. inline(
  176.  
  177. $c4/$7e/<s/                {les di,s[bp]            (es:di points to array)  }
  178. $8b/$46/<initial_crc/      {mov ax,initial_crc[bp]  (initial CRC value)      }
  179. $8b/$4e/<s_length/         {mov cx,s_length[bp]     (count)                  }
  180. $be/table_256/             {mov si,offset table_256 (table address)          }
  181.  
  182.  
  183. { next:  }
  184.  
  185. $26/$32/$05/               {xor al,es:[di]          CRC = CRC XOR next byte  }
  186. $47/                       {inc di                  (point to next byte)     }
  187.  
  188. { intermediate steps, see comments for overall effect }
  189.  
  190. $31/$db/                   {xor bx,bx               (bx <- 0)                }
  191. $86/$d8/                   {xchg al,bl              (bx <- ax and 0FF)       }
  192. $86/$e0/                   {xchg al,ah              (ax <- ax shr 8)         }
  193. $d1/$e3/                   {shl bx,1                (bx <- bx+bx)            }
  194.  
  195. $33/$00/                   {xor ax,[bx+si]          CRC = (CRC shr 8) XOR
  196.                                                           table[CRC and 0FF] }
  197.  
  198. $e2/$f0/                   {loop next               (count <- count -1)      }
  199.  
  200. $89/$46/<s+4);             {mov s+4[bp],ax          (crc_string_256 := CRC)  }
  201.  
  202.  
  203. {  basic algorithm expressed above
  204.  
  205. crc := initial_crc
  206.  
  207. For each byte Do
  208. Begin
  209.   crc := crc XOR next_byte;
  210.   crc := (crc shr 8) XOR table_256 [crc and $FF];
  211. End;
  212.  
  213. crc_string_256 := crc;
  214. }
  215. End;
  216.  
  217.  
  218.  
  219. Procedure set_attr (attr : byte; asciiz : string255);
  220. {
  221.  
  222.   This routine sets the file attributes.  Uses Function $43 in
  223.   Interrupt $21.
  224.  
  225.   Turbo Pascal is unable to open and read various types files
  226.   (e.g., r/o and files that are both hidden and system).  This
  227.   gets around that by always setting the attribute to 0, then
  228.   reseting it to the original value.
  229.  
  230.   attr  is the attribute to be set on the file
  231.   asciiz is a string variable with the file name
  232.  
  233. }
  234.  
  235. begin
  236.   asciiz := asciiz + chr(0);  {  Make a valid DOS ASCIIZ name  }
  237.   {  Set up the registers for the interrupt  }
  238.   global_reg.ax := $4301;
  239.   global_reg.ds := seg(asciiz);
  240.   global_reg.dx := ofs(asciiz)+1;
  241.   global_reg.cx := attr;
  242.   intr ($21, global_reg);
  243. end;
  244.  
  245.  
  246. Procedure get_crc(this_file : string255; dta : DTA_record);
  247. {
  248.   This procedure computes the CRC for a file.  Value is returned
  249.   in the global variable CRC_value.
  250.  
  251.   this_file is a string variable containing the file name
  252.   dta is a DTA_Record containing the file's DTA
  253.  
  254. }
  255.  
  256. var
  257.   length  : real;  {  Length of the File  }
  258.  
  259. begin
  260.  
  261.   {  Change the Attribute byte so we can always open it  }
  262.   {    To save some time, this is only done if the file  }
  263.   {    Has any attribute other than ARCHIVE              }
  264.  
  265.   if (dta.attribute and $DF <> 0) then
  266.     set_attr ( 0, this_file);
  267.  
  268.   {  Get the size of the file  }
  269.  
  270.   if dta.low_size < 0 then
  271.     {  Negative low_size is really number between 32768 and 65536  }
  272.     length := int(dta.high_size)*65536.0 + 32768.0
  273.               + int(dta.low_size and $7FFF)
  274.   else
  275.     length := int(dta.high_size)*65536.0 + int(dta.low_size);
  276.  
  277.   {  Open the file as untyped  }
  278.  
  279.   Assign (Filvar, this_file);
  280.   Reset (Filvar);
  281.  
  282.   {  Calculate the CRC  }
  283.  
  284.   CRC_value := 0;
  285.   While length > 0.5 do
  286.   Begin
  287.     {  Read a segment of the file to process  }
  288.     BlockRead(filvar,byte_string,BufSize,RecsRead);
  289.     {  Get the correct number of bytes to process  }
  290.     if length >= Buffer_Length then
  291.       str_length := Buffer_Length
  292.     else
  293.       str_length := round(length);
  294.     {  Compute the CRC  }
  295.     CRC_value := crc_string_256(byte_string, str_length, CRC_value);
  296.     {  Adjust the file length  }
  297.     length := length - Buffer_Length;
  298.   End;
  299.  
  300.   Close (Filvar);
  301.  
  302.     {  Restore the correct Attribute Byte  }
  303.  
  304.   if (dta.attribute and $DF <> 0) then
  305.     set_attr ( dta.attribute, this_file);
  306.  
  307. end;
  308.  
  309.  
  310. Procedure directory(current_directory : string255);
  311.  
  312. {
  313.   Procedure to calculate the CRC of all the files in a directory,
  314.   then all subdirectories in that directory
  315.  
  316.   current_directory contains the directory name (including drive)
  317.  
  318. }
  319.  
  320. var
  321.   DTA_ofs, DTA_seg : integer;  {  Contains the current DTA address  }
  322.   reg : Registers;  {  Local 8088/8086/80286 registers  }
  323.   DTA : DTA_record;  {  Local DTA  }
  324.   this_directory, this_file, asciiz : string255;  { directory and file names }
  325.  
  326.  
  327. function get_file : string255;
  328.  
  329. {  Get the file name from the DTA  }
  330.  
  331. var
  332.   i : integer;
  333.   temp_file : string255;
  334.  
  335. begin
  336.   i := 1;
  337.   temp_file := '';
  338.   repeat
  339.     temp_file := temp_file + DTA.filename[i];
  340.     i := i+1;
  341.   until dta.filename[i] = chr(0);
  342.  
  343.   get_file := temp_file;
  344.  
  345. end;
  346.  
  347.  
  348. function is_directory : boolean;
  349.  
  350. {  Function to tell if the file is a directory entry  }
  351.  
  352. begin
  353.   is_directory := ((dta.attribute and $10) <> 0)
  354.                    and (dta.filename[1] <> '.');
  355. end;
  356.  
  357. Procedure set_DTA(offset, segment : integer);
  358.  
  359. {   sets the disk DTA
  360.     Uses MSDOS Function $1A with interrupt $21
  361.     offset is the offset of the new DTA
  362.     segment is the segment of the new DTA
  363. }
  364.  
  365. begin
  366.   reg.ax := $1a00;
  367.   reg.ds := segment;
  368.   reg.dx := offset;
  369.   intr($21, reg);
  370. end;
  371.  
  372. Procedure get_DTA(var offset, segment : integer);
  373.  
  374. {   gets the disk DTA
  375.     Uses MSDOS Function $2F with Interrupt $21
  376.     offset will return with the current DTA offset
  377.     segment will return with the current DTA segment
  378. }
  379.  
  380. begin
  381.   reg.ax := $2f00;
  382.   intr($21, reg);
  383.   offset := reg.bx;
  384.   segment := reg.es;
  385. end;
  386.  
  387.  
  388. Function find_first (attr_mask : byte) : boolean;
  389.  
  390. {
  391.     Find the first file matching the ASCIIZ string.
  392.     attr_mask is $27 for files only and $37 for directories & files
  393.  
  394.     INT 21 function 4EH
  395.     Returns TRUE if found, FALSE if not found
  396. }
  397.  
  398. begin
  399.   reg.ax := $4e00;
  400.   reg.ds := seg(asciiz);
  401.   reg.dx := ofs(asciiz)+1;
  402.   reg.cx := attr_mask;
  403.   intr($21, reg);
  404.   find_first := (lo(reg.ax) <> 18);
  405.  
  406. end;
  407.  
  408.  
  409. Function find_next (attr_mask : byte) : boolean;
  410.  
  411. {
  412.     Find the next file matching the ASCIIZ string.
  413.     attr_mask is $27 for files only and $37 for directories & files
  414.  
  415.     Returns TRUE if found, FALSE if not found
  416. }
  417.  
  418. begin
  419.   reg.ax := $4f00;
  420.   reg.cx := attr_mask;
  421.   intr($21, reg);
  422.   find_next := (lo(reg.ax) <> 18);
  423. end;
  424.  
  425.  
  426. begin { directory }
  427.  
  428.   get_DTA(DTA_ofs, DTA_seg); { Save the current DTA location }
  429.  
  430.   set_DTA(ofs(DTA), seg(DTA)); { Set the DTA location to local area }
  431.  
  432. {
  433.   Find and print the files in the current directory
  434. }
  435.  
  436.   asciiz := current_directory + '\*.*' + CHR(0);  {  CHR(0) to make proper  }
  437.  
  438. {  Process all the files before doing any directories  }
  439.  
  440.   if find_first ($27) then
  441.     repeat
  442.       if dta.filename[1] <> '.' then
  443.         all_done := true;
  444.         for i := 1 to test_total do
  445.           if not test_done[i] then all_done := false;
  446.         begin
  447.           this_file := get_file;
  448.           if diagnostics then
  449.             write (current_directory,'\',this_file,':');
  450.           doit := false;
  451.           for i := 1 to test_total do
  452.             if current_directory+'\'+this_file = test_files[i] then
  453.               begin
  454.               doit := true;
  455.               test_done[i] := true;
  456.               end;
  457.           if doit then
  458.             begin
  459.             get_crc(current_directory + '\' + this_file, dta);
  460.             writeln(outfile,current_directory,' ',this_file,' ',
  461.                 dta. attribute,' ',dta.time_of_day,' ',dta.date,' ',
  462.                 dta.low_size,' ',dta.high_size,' ',CRC_value);
  463.             if diagnostics then writeln ('   *');
  464.             end
  465.             else
  466.             if diagnostics then writeln (all_done);
  467.         end;
  468.     until not find_next ($27);
  469.  
  470. {  Now process all the directories  }
  471.  
  472.   if find_first ($37) then
  473.     repeat
  474.       if is_directory then
  475.       begin
  476.         this_directory := current_directory + '\' + get_file;
  477.         if diagnostics then
  478.           Writeln(this_directory);
  479.         if not all_done then directory(this_directory);
  480.       end;
  481.     until not find_next ($37);
  482.  
  483.   set_dta(DTA_ofs, DTA_seg); { restore the old DTA }
  484.  
  485. end;
  486.  
  487.  
  488. Function current_drive : byte;
  489. {
  490.   Function to return the current drive
  491.   Uses MSDOS Function $19 with Interrupt $21
  492.   current_drive is 1 if A, 2 if B, 3 if C, etc.
  493.  
  494. }
  495.  
  496. begin
  497.   global_reg.ax := $1900;
  498.   intr($21, global_reg);
  499.   current_drive := 1 + lo(global_reg.ax);
  500. end;
  501.  
  502.  
  503. BEGIN  {  FILECRC, main program  }
  504.  
  505.   {  root will have the current drive designation  }
  506.   root := chr(current_drive + ord('A') - 1) + ':';
  507.   diagnostics := false;
  508.   assign (test_file,'FILETEST.FIL');
  509.   {$I-}
  510.   Reset (test_file);   {  See if FILETEST.FIL exists  }
  511.   {$I+}
  512.   if IOresult <> 0 then goto allover;
  513.   test_total := 1;
  514.   while not eof(test_file) do
  515.     begin
  516.     readln(test_file,test_files[test_total]);
  517.     test_done[test_total] := false;
  518.     if diagnostics then
  519.       writeln(':',test_files[test_total],': ',test_total);
  520.     if test_files[test_total] = 'C:\?' then
  521.       begin
  522.       test_total := test_total -1;
  523.       diagnostics := true;
  524.       Writeln('CRC file integrity program');
  525.       Writeln('Version ',version:5:2,', ',version_date);
  526.       Write('Written by Ted H. Emigh -- ');
  527.       Writeln('emigh@ncsugn.uucp or NEMIGH@TUCC.BITNET');
  528.       Writeln('Modified by L. P. Levine -- University of Wisconsi-Milwaukee');
  529.       end;
  530.     test_total := test_total + 1;
  531.     end;
  532.   test_total := test_total - 1;
  533.   Assign (filvar,'FILETEST.OLD');
  534.   {$I-}
  535.   Reset (filvar);   {  See if FILETEST.OLD exists  }
  536.   {$I+}
  537.   {  check_crc will be TRUE if FILETEST.OLD exists  }
  538.   check_crc := (IOresult = 0);
  539.   if check_crc then
  540.   begin
  541.     Assign (outfile,'FILETEST.NEW');
  542.     if diagnostics then
  543.       Writeln ('Creating File FILETEST.NEW');
  544.   end
  545.   else
  546.   begin
  547.     Assign (outfile,'FILETEST.OLD');
  548.     if diagnostics then
  549.       Writeln ('Creating File FILETEST.OLD');
  550.   end;
  551.   Close (filvar);
  552.   Rewrite (outfile);  {  Open the output file  }
  553.  
  554.   Generate_table_256(POLY);  {  Generate the table for CRC check  }
  555.  
  556.   if diagnostics then
  557.     Writeln(root+'\');
  558.   directory(root);  {  Now, do the CRC check  }
  559.   if not all_done then
  560.     begin
  561.     writeln('Not all of the files you listed were found.');
  562.     writeln;
  563.     writeln ('Files not found:');
  564.     for i := 1 to test_total do
  565.       if not test_done[i] then writeln (test_files[i]);
  566.     end;
  567.   Close (outfile);
  568. allover:
  569. end.
  570.  
  571.