[an error occurred while processing this directive]

Система контроля целостности данных

В данной статье мы разработаем систему контроля целостности данных. Наша системы контроля данных будет следить за изменениями файлов в заданной директории. Контролю будут подлежать следующие виды изменений: удаление, создание, перемещение и изменения файлов.

Разрабатывать нашу систему контроля целостности данных мы будем в среде визуального программирования Delphi 6, с использованием серерва БД Interbase SQL Server 6.0.

Необходимо заметить, что реальная система контроля целостности данных должна отслеживать гораздо больше изменений, но для описания общих принципов построения такой системы нам будет достаточно перечисленных выше контролируемых изменений.

Для учета изменений, происходящих в файловой системе лучше всего использовать базу данных, т.к. это позволит упростить задачу получения требуемых выборок данных.

Проектирование структуры БД начинается с формализации данных, учитываемых в ней. Для решения поставленной задачи вспомним основные характеристики файлов, хранящихся в файловой системе. Основные характеристики файлов, учитываемые при их хранении:

  • Имя файла (до 256 символов)
  • Директория размещения файла (до 256 символов)
  • Тип файла (расширение) (до 4 символов)
  • Дата создания (изменения, последнего обращения к файлу)
  • Размер файла (длинное целое до 2'000'000'000 байт)
  • Атрибуты файла (байт)
  • Контрольная сумма данных файла (необходимо высчитывать отдельно)
  • Для хранения всех описанных характеристик файлов создадим БД. Ниже приведен скрипт необходимой БД. После его компиляции будет создан файл FILE.GDB.

    
    CREATE DATABASE "file.GDB" 
    
    USER "SYSDBA" PASSWORD "masterkey"
    
    PAGE_SIZE 2048 DEFAULT CHARACTER SET WIN1251;
    
    SET NAMES WIN1251;
    
    /* ТАБЛИЦА ХРАНИНИЯ ИНФОРМАЦИИ О ЗАЩИЩЕННЫХ ФАЙЛАХ */
    
    CREATE TABLE TABLE1
    
    /* ИДЕНТЕФИКАТОР ЗАПИСИ */
    
    (ID                     INTEGER                   NOT NULL      PRIMARY KEY, 
    
    /* ИМЯ ФАЙЛА */
    
    FILE_NAME               VARCHAR(255)              NOT NULL,
    
    /* ПУТЬ К ФАЙЛУ */
    
    FULL_PATH               VARCHAR(1024)             NOT NULL,
    
    /* РАЗМЕР ФАЙЛА */
    
    FILE_SIZE               INTEGER                   NOT NULL,
    
    /* ДАТА СОЗДАНИЯ (ИЗМЕНЕНИЯ) ФАЙЛА */
    
    DATA                    VARCHAR(9)                NOT NULL,
    
    /* АТРИБУТЫ ФАЙЛА */
    
    ATR                     VARCHAR(3)                NOT NULL,
    
    /* КОНТРОЛЬНАЯ СУММА ПО ПЕРВОМУ АЛГОРИТМУ */
    
    CRC1                    INTEGER                   NOT NULL,
    
    /* КОНТРОЛЬНАЯ СУММА ПО ВТОРОМУ АЛГОРИТМУ */
    
    CRC2                    INTEGER                   NOT NULL
    
    );
    
    COMMIT;
    
    /* ТАБЛИЦА ДЛЯ ХРАНЕНИЯ ВРЕМЕННОЙ ИНФОРМАЦИИ О ЗАЩИЩАЕМИЫХ ФАЙЛАХ ПРИ ПРОВЕРКЕ */
    
    CREATE TABLE TABLE2
    
    /* ИДЕНТЕФИКАТОР ЗАПИСИ */
    
    (ID                   INTEGER                   NOT NULL      PRIMARY KEY, 
    
    /* ИМЯ ФАЙЛА */
    
    FILE_NAME             VARCHAR(255)              NOT NULL,
    
    /* ПУТЬ К ФАЙЛУ */
    
    FULL_PATH             VARCHAR(1024)             NOT NULL,
    
    /* РАЗМЕР ФАЙЛА */
    
    FILE_SIZE             INTEGER                   NOT NULL,
    
    /* ДАТА СОЗДАНИЯ (ИЗМЕНЕНИЯ) ФАЙЛА */
    
    DATA                  VARCHAR(9)                NOT NULL,
    
    /* АТРИБУТЫ ФАЙЛА */
    
    ATR                   VARCHAR(3)                NOT NULL,
    
    /* КОНТРОЛЬНАЯ СУММА ПО ПЕРВОМУ АЛГОРИТМУ */
    
    CRC1                  INTEGER                   NOT NULL,
    
    /* КОНТРОЛЬНАЯ СУММА ПО ВТОРОМУ АЛГОРИТМУ */
    
    CRC2                  INTEGER                   NOT NULL
    
    );
    
    COMMIT;
    
    
    
    CREATE TABLE TAB
    
    /* ИДЕНТЕФИКАТОР ЗАПИСИ */
    
    (ID                       INTEGER            NOT NULL      PRIMARY KEY, 
    
    /* ИМЯ ФАЙЛА */
    
    FILE_NAME                VARCHAR(1024)       NOT NULL ,
    
    EXT                      VARCHAR(300)        NOT NULL
    
    );
    
    COMMIT;
    
     

    Теперь необходимо рассмотреть каким образом мы будем заполнять нашу БД, т.е. как получить все описанные выше характеристики файлов. Прежде всего необходимо определить для каких файлов нам необходимы эти характеристики. Т.е. какие файлы подлежат контролю. Нам необходимо контролировать все файлы, которые находятся в указанной директории а также во всех поддиректориях выбранной директории. А также иногда бывает необходимо контролировать не все файлы в директории, а только определенного вида, например документы MS Word. Следовательно нам необходимо задавать маску для файлов, которые подлежат контролю. Также при поиске файлов в заданной директории необходимо учесть следующие аспекты:

  • Файловая система кроме самих имен поддиректорий хранит также указатели на саму себя и на родительскую директорию, которые имеют зарезервированные имена "." и "..", естественно, что эти имена обрабатывать не нужно.
  • Директории также как и файлы могут иметь атрибуты, которые будут скрывать ряд имен от неправильно построенного вызова процедуры поиска файлов (директория тоже файл).
  • Приведенная ниже процедура производит поиск всех файлов удоволетворяющих заданной маске в указанной директории и во всех ее поддиректориях.

    
    procedure find_file (path:string;mask:string);
    
    var
    
    sr:tsearchrec;
    
    begin
    
        if (findfirst (path+'\*.*',63,sr)=0) then
    
       repeat
    
          if (sr.Name<>'.')and(sr.Name<>'..') then
    
             if dir(sr.Attr) then find_file (path+'\'+sr.Name,mask)
    
               else
    
               if ext_in_mask(AnsiLowerCase(extractFileExt(sr.Name)),AnsiLowerCase(mask)) then
    
                  ins(dm1.IBQuery1,path,sr);
    
       until not(findnext(sr)=0);
    
    end;
    
    

    Данная процедура использует для своей работы функцию ext_in_mask(ext:string;mask:string), которая проверяет принадлежность маски файла к списку заданных пользоввателем масок файлов, и функцию dir(att:integer), которая проверяет является ли найденный файл директорией или нет.

    
    function ext_in_mask(ext:string;mask:string):boolean;
    
    var i:integer;
    
        str:string;
    
    begin
    
       if mask='*' then
    
       begin
    
          result:=true;
    
          exit
    
       end;
    
       for i:=1 to length(mask) do
    
          if mask[i]<>',' then  str:=str+mask[i]
    
          else if ext='.'+str then
    
                  begin
    
                     result:=true;
    
                     exit
    
                  end
    
               else str:='';
    
       if ext='.'+str then result:=true
    
       else result:=false
    
    end;
    
    

    После обнаружения нужного файла прцедура вызывает процедуру вставки конторлируемых параметров обнаруженного файла в БД.

    
    procedure ins(ibq: TIBQuery;path:string;sr:tsearchrec);
    
    var
    
    ident:integer;
    
    fn:string;
    
    begin
    
       fn:=path+'\'+sr.Name;
    
       ibQ.Close;
    
       ibQ.SQL.Clear;
    
       ibq.SQL.Add('select max(id) from table1');
    
       ibq.Open;
    
       if ibq.FieldValues['max']<>null then
    
         ident:=strtoint(ibq.FieldValues['max'])+1
    
         else ident:=1;
    
       ibq.Close;
    
       ibq.SQL.Clear;
    
       ibq.SQL.Add('insert into table1 values ('+inttostr(ident)+','''+sr.name+''',''
    
       '+path+''','   +inttostr(sr.size)+','''+intToStr(sr.time)+''','''
    
       +get_attr(sr.Attr)+''','+inttostr(crc(fn))+','   +inttostr(crcsumm(fn))+')');
    
       ibq.execsql;
    
    end;
    
    

    Как видно из листинга процедура все контролируемые параметры файла могут быть получены, используя стандартные функции, кроме контрольной суммы файла. Т.е. все характеристики кроме последнейе хранится в файловой системе, поэтому рассмотрим задачу вычисления контрольной суммы более подробно.

    Осуществить подсчет контрольных сумм файлов можно несколькими способами:

  • Методом сложения по модулю 2 всех блоков данных файла. Задать длину блока данных, равной 32 битам. Последний, неполный блок данных, если необходимо, дополнять нулями.
  • Методом деления данных файла, представленных в виде полинома на полином. Т.к. выбор полинома достаточно сложная задача, то возьмем полином, применяемый в рекомендации X.25 (х16+х12+х5+1).
  • Подсчитать Хэш функцию по данным файла по одному из стандартных алгоритмов MD2, MD4, MD5 или SHA.
  • Каждый из предложенных методов имеет свой круг недостатков. Третий метод наиболее подходит для решения поставленной задачи, однако требует хорошей математической подготовки и времени на разработку программы, поэтому использовать его не будем. Два первых метода каждый в отдельности достаточно легко обходятся, однако решение задачи модификации данных, удовлетворяющее обоим методам является нетривиальной задачей, поэтому остановимся на реализации расчета контрольных сумм по первым двум алгоритмам.

    Ниже приведен листинг двух функций, вычисляющих контрольную сумму файла по первому и второму методу соответственно.

    Данный функции испольуют бибилотеку обработки битовых последовательностей StrBit32.
    
      function crcsumm(namefile:string):integer;
    
    var
    
      f:Tbitfile;
    
    //переменная для открытия файла в битовом формате
    
      nul,summ:tstr_bit;
    
    //nul-переменная для дополнения последнего блока до 32 бит
    
    //summ-переменная для последовательного суммирования блоков
    
      rasm:integer;
    
    
    
    begin
    
      f:=tbitfile.create;
    
      summ:=tstr_bit.Create;
    
      nul:=tstr_bit.Create;
    
      f.OpenBitFile(namefile,btOpenRead,bt8);
    
      f.ChangeOrderBit;// изменение порядка следования бит в переменной
    
      summ.Init_0(32);//первоначальное значение СРС
    
      nul.Init_0(8); //заполнение добавочного байта нулями
    
      repeat// чтение и суммирование пока блок =32бита
    
          f.ReadStr(32); //чтение32-х битовогоблока
    
          rasm:=f.Size;//определение количества считанных бит
    
          while f.Size<32 do //добавление до 32 бит
    
            f.Concat(nul);
    
          summ.BOOL(f,btxor);//суммирование блоков
    
      until rasm<32;
    
      crcsumm:=summ.Nomer(0,32);//приведение битовой комбинации к десятичному виду
    
      f.CloseBitFile;
    
      f.Free;
    
      summ.Free;
    
      nul.Free;
    
    end;
    
    
    
    
    
    function crc(namefile:string):integer;
    
    var
    
     ff:tbitfile;
    
     rasm,ind:longint;
    
     polinom,sum:tstr_bit;
    
     flag:boolean;
    
    begin
    
      ff:=tbitfile.Create;
    
      polinom:=tstr_bit.Create;
    
      sum:=tstr_bit.Create;
    
      ff.OpenBitFile(namefile,btOpenRead,bt8 );
    
      rasm:=ff.SizeOfFile;
    
      sum.Init_0(17);
    
      polinom.Init_Sim('10001000000100001');
    
      ff.ReadStr(17);
    
      sum.CopyAllBit(ff);
    
      if rasm>17 then
    
       begin
    
         repeat
    
           sum.InvStrBit;
    
           ind:=sum.OpenBit;
    
           sum.InvStrBit;
    
           if ind<>0 then
    
             begin
    
               ff.ReadStr(ind);
    
               sum.Concat(ff);
    
               sum.Delete(0,ind);
    
             end;
    
           if sum.Size>=17 then
    
            begin
    
             sum.BOOL(polinom,btxor);
    
             flag:=true;
    
            end
    
           else flag:=false;
    
         until flag=false;
    
       end;
    
      if rasm<>0 then crc:=sum.Nomer(0,sum.Size)
    
      else crc:=0;
    
      ff.CloseBitFile;
    
      polinom.Free;
    
      sum.Free;
    
    end;
    
    

    Вот практически и все. Все основные функции для полулечения контролируемых параметров файлов и занесения их в БД описаны. Проверка контролируемых директорий осуществляется аналогичным способом, только контролируемые параметры проверяемых файлов заносятся во временную таблицу в БД, и после окончания проверки эта таблица удаляется. После занесения проверяемых файлов во временную таблицу запускается функция сравнения контролируемых атрибутов.

    
    function FileIzmenen(Baza:TIBQuery;FullName:string):string;
    
    var
    
        sql, NF, PP,DI, otvet : string;
    
        R: Integer;
    
        st: TSearchRec;
    
    begin
    
      otvet:='';
    
       NF:= ExtractFileName(FullName);        // sohranayem
    
      PP:= ExtractFileDir(FullName);
    
      //delete(pp,length(pp)+1,1);
    
      FindFirst( FullName,faAnyFile,st);
    
       r:=st.size;
    
    
    
      DI:=inttostr(st.Time);
    
      sql:=baza.SQL.Text;
    
      baza.Close;
    
      baza.SQL.Clear;
    
      baza.SQL.Add('select count(*) from TABLE1 where   file_name=''' + nf 
    
                    +''' and full_path='''+PP+''' and file_size='''
    
    				+inttostr(r)+'''');
    
      baza.Open;
    
      if  baza.FieldValues['count']=0 then otvet:=otvet+'S';
    
      baza.Open;
    
      baza.Close;
    
      baza.SQL.Clear;
    
      baza.SQL.Add(sql);
    
      baza.Open;
    
      baza.Close;
    
      baza.SQL.Clear;
    
      baza.SQL.Add('select count(*) from TABLE1 where   
    
                   file_name=''' + nf +''' and full_path='''+PP
    
                   +''' and atr='''+get_attr(st.Attr)+'''');
    
      baza.Open;
    
      if  baza.FieldValues['count']=0 then otvet:=otvet+'A';
    
      baza.Open;
    
    
    
      baza.Close;
    
      baza.SQL.Clear;
    
      baza.SQL.Add(sql);
    
      baza.Open;
    
    
    
      baza.Close;
    
      baza.SQL.Clear;
    
      baza.SQL.Add('select count(*) from TABLE1 where   file_name=''' + nf 
    
                   +''' and full_path='''+PP+''' and Crc1='+inttostr(Crc(FullName))
    
    			   +' and Crc2='+inttostr(CrcSumm(FullName)));
    
      baza.Open;
    
      if  baza.FieldValues['count']=0 then otvet:=otvet+'C';
    
      FileIzmenen:= otvet;
    
    end;
    
    

    Данная функция формирует отчет о всех произошедших изменениях с контролируемыми файлами.

    Вот, в принципе, простая система контроля целостности данных готова. Полную версию рассмотренного программного продукта с исходным кодом можно скачать здесь (zip-архив 30 Kb).

    О всех замеченных недостатках и ошибках, а также свои вопросы и пожелания пишите автору.