В данной статье мы разработаем систему контроля целостности данных. Наша системы контроля данных будет следить за изменениями файлов в заданной директории. Контролю будут подлежать следующие виды изменений: удаление, создание, перемещение и изменения файлов.
Разрабатывать нашу систему контроля целостности данных мы будем в среде визуального программирования 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).
О всех замеченных недостатках и ошибках, а также свои вопросы и пожелания пишите автору.