В данной статье мы разработаем систему контроля целостности данных. Наша системы контроля данных будет следить за изменениями файлов в заданной директории. Контролю будут подлежать следующие виды изменений: удаление, создание, перемещение и изменения файлов.
Разрабатывать нашу систему контроля целостности данных мы будем в среде визуального программирования Delphi 6, с использованием серерва БД Interbase SQL Server 6.0.
Необходимо заметить, что реальная система контроля целостности данных должна отслеживать гораздо больше изменений, но для описания общих принципов построения такой системы нам будет достаточно перечисленных выше контролируемых изменений.
Для учета изменений, происходящих в файловой системе лучше всего использовать базу данных, т.к. это позволит упростить задачу получения требуемых выборок данных.
Проектирование структуры БД начинается с формализации данных, учитываемых в ней. Для решения поставленной задачи вспомним основные характеристики файлов, хранящихся в файловой системе. Основные характеристики файлов, учитываемые при их хранении:
Для хранения всех описанных характеристик файлов создадим БД. Ниже приведен скрипт необходимой БД. После его компиляции будет создан файл 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;
Как видно из листинга процедура все контролируемые параметры файла могут быть получены, используя стандартные функции, кроме контрольной суммы файла. Т.е. все характеристики кроме последнейе хранится в файловой системе, поэтому рассмотрим задачу вычисления контрольной суммы более подробно.
Осуществить подсчет контрольных сумм файлов можно несколькими способами:
Каждый из предложенных методов имеет свой круг недостатков. Третий метод наиболее подходит для решения поставленной задачи, однако требует хорошей математической подготовки и времени на разработку программы, поэтому использовать его не будем. Два первых метода каждый в отдельности достаточно легко обходятся, однако решение задачи модификации данных, удовлетворяющее обоим методам является нетривиальной задачей, поэтому остановимся на реализации расчета контрольных сумм по первым двум алгоритмам.
Ниже приведен листинг двух функций, вычисляющих контрольную сумму файла по первому и второму методу соответственно. Данный функции испольуют бибилотеку обработки битовых последовательностей 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).
О всех замеченных недостатках и ошибках, а также свои вопросы и пожелания пишите автору.