Posts on this page:
После небольшого перерыва продолжаю допиливать свой вариант FCIV на PowerShell. И радостно могу сообщить, что уже есть версия 1.0, т.е. полностью отвечающая нашим требованиям. Что изменилось в новой версии?
А теперь и on-line справка по всем параметрам по просьбе трудящихся.
Несколько примеров использования:
Start-PsFCIV C:\Files db.xml -SHA1 -Recurse -Show Bad, Missed
будет проверена папка C:\Files и все вложенные папки. Файл db.xml должен быть размещён непосредственно в этой папке. Если файл не существует, то будет создан с нуля. После проверки будет показано графическое окно с именами файлов, которые попали в категорию Bad и Missed. Для каждой категории будет отдельное графическое окно.
Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Include data.dat
будет проверен только файл data.dat в папке C:\Files с использованием SHA1 алгоритмом хешиования. Если для файла в БД записан только MD5 хеш, то проверка будет произведена с использованием MD5. Если файл БД (db.xml) не существует, то создастся новый файл БД со сведениями о файле data.dat. Файл будет подсчитан с использованием как SHA1, так и MD5.
Start-PsFCIV C:\Files db.xml -SHA1 -MD5 -Rebuild
будет произведено освежение файла БД для папки C:\Files. Все записи, для которых соответствующего файла не обнаружено, будут удалены. Если в папке есть файлы, для которых нет соответствующей записи, то они будут обсчитаны с использованием алгоритмов SHA1 и MD5 и будут добвлены в XML файл. Файл db.xml должен существовать, иначе скрипт вернёт фатальную ошибку.
Start-PsFCIV C:\Files db.xml -SHA1 -Quiet
Папка C:\Files будет проверена в несопровождаемом режиме с использованием алгоритма SHA1. По умолчанию никакой информации на экране не будет. После окончания работы, в зависимости от результатов проверки, скрипт сгенерирует соответствующий код возврата (0-5).
И, собственно, сам скрипт:
И как обычно, любые замечания, комментарии постить в каменты.
И я снова вернулся. После последнего анализа кода была обнаружена серьёзная ошибка в коде, из-за которой пришлось его очень сильно переписывать. Поэтому я предлагаю оценить RC версию скрипта, который уже выполняет свою задачу, которая расписана в предыдущих статьях.
Итак, как с этим скриптом начинать работать. В скрипте есть 3 обязательных параметра: путь к папке, которую нужно посчитать или проверить, путь к XML файлу (если файла нету, то он будет создан) и как минимум 1 алгоритм хеширования. Т.е. минимум это должно выглядеть вот так:
.\PSFCIV_0.85.ps1 C:\Temp –xml DB.XML –sha1
При этом можно указать алгоритм –md5 или оба сразу (-sha1 –md5). В таком случае для каждого файла будет сгенерировано 2 хеша. Без указания любого из этих параметров будет сгенерирована ошибка и работа будет остановлена.
Опциональные параметры:
На данном этапе реализована работа в соответствии с логикой (которая описана тут: PS FCIV (часть 1)) и односторонняя совместимость с FCIV. Т.е. этой утилите можно скармливать XML файл, который сгенерирован с помощью этого скрипта. В скрипте начата реализация поддержки альтернативных БД, которые не совместимы с FCIV XML. Плюс планируется обеспечение двусторонней поддержки FCIV – т.е. скрипт сможет корректно воспринимать и обрабатывать XML файлы, которые сгенерированы утилитой FCIV. Ну и оптимизация самого кода. Сам скрипт снабжён достаточно плотными комментариями, поэтому будет совсем нетрудно с ним разобраться. По мере дописывания кода буду выкладывать более актуальные версии файла.
Update 27.07.2009
Продолжаем повествование о процессе разработки функционального аналога FCIV.
Как я уже упоминал, самая первая и основная функциональная часть – функция подсчёта хешей. Я уже делал скрипт, который считает хеши: Полезная безделушка Hash SHA1 на PowerShell, который и стал основой моего скрипта. Однако, по одной причине саму считалку пришлось обрезать. Если посмотреть справку по FCIV (http://support.microsoft.com/kb/841290), то он не записывает в БД реальный хеш файла, а пропускает его ещё через Base64 кодировку и закодированный хеш уже записывается в XML файл. В .NET уже есть стандартный кодировщик Base64, который на входе принимает массив байтов. Если посмотреть на указанный пост из моего блога, то там я приводил вывод объекта $hasher, который как раз и является байтовым массивом, который мы потом преобразовывали в строку. Здесь же преобразовывать в строку ничего не нужно, а сразу подаём этот массив в конвертер. Вот как выглядит функция подсчёта хеша:
function _hashbytes_ ($type, $file) { switch ($type) { "sha1" {$hasher = [System.Security.Cryptography.SHA1]::Create()} "md5" {$hasher = [System.Security.Cryptography.MD5]::Create()} } $inputStream = New-Object System.IO.StreamReader ($file) $hashBytes = $hasher.ComputeHash($inputStream.BaseStream) $inputStream.Close() $hashBytes }
в качестве аргументов мы принимаем тип хеша – MD5 или SHA1 и сам файл:
[↓] [vPodans] _hashbytes_ sha1 wsr.txt 7 11 122 247 126 249 232 24 193 38 194 124 237 202 26 183 178 64 173 45 [↓] [vPodans] [System.Convert]::ToBase64String($(_hashbytes_ sha1 wsr.txt)) Bwt693756BjBJsJ87coat7JArS0= [↓] [vPodans] [System.Convert]::ToBase64String($(_hashbytes_ md5 wsr.txt)) kshzMfHPdtuXdL6l/vWwPA== [↓] [vPodans]
я подсчитал SHA1 хеш для текстового файла и на выходе получил такой же по структуре массив байтов, как и в предыдущей статье. И далее я этот массив подал в качестве аргумента для конвертера Base64 строки (статический метод ToBase64String класса System.Convert), а на выходе уже получил правильную Base64 строку. Именно эту строку FCIV записывает в файл. То же самое будем делать и мы. Кстати говоря. обратное преобразование делается через тот же класс System.Convert, но с использованием метода FromBase64String:
[↓] [vPodans] [System.Convert]::FromBase64String("Bwt693756BjBJsJ87coat7JArS0=") 7 11 122 247 126 249 232 24 193 38 194 124 237 202 26 183 178 64 173 45 [↓] [vPodans] -join ([System.Convert]::FromBase64String("Bwt693756BjBJsJ87coat7JArS0=") | %{"{0:X2}" -f $_}) 070B7AF77EF9E818C126C27CEDCA1AB7B240AD2D [↓] [vPodans]
как видите, массив байтов получился точно такой же. Второй строкой я показал, как можно собрать строку самого хеша, которую показывают различные программы подсчёта хешей. Как можно заметить все числа в массиве меньше, чем 256, т.е. не превышают значения 28, что является 1 байтом в двоичной форме. В цикле Foreach-Object я преобразовываю каждый байт в HEX и собираю в единую строку оператором –Join, который доступен только в PowerShell V2. Но можно собрать и через [System.String]::Join() для PowerShell 1.0.
Если посмотреть в статью по FCIV, то увидим, что он использует свою нестандартную XML схему:
<?xml version="1.0" encoding="utf-8" ?> <FCIV> <FILE_ENTRY> <name>wsr.txt</name> <SHA1>Bwt693756BjBJsJ87coat7JArS0=</SHA1> </FILE_ENTRY> <FILE_ENTRY> <name>0</name> <SHA1>xd/Rh0JyuW/I6sxPzY4sULyjS38=</SHA1> </FILE_ENTRY> </FCIV>
По этой причине мы лишаемся возможности использования Export-CliXML и подобных команд, а нужно собирать вручную.
Внимание: хоть FCIV и непривередлив к дополнительным полям в XML, но он начинает сильно ругаться, если поле Name написано с большой буквы. Его нужно заполнять так, как он есть в экземпляре, т.е. все маленькие буквы.
Объект, который будет хранить все данные о файле будет вот такой:
$object = "" | Select Name, Size, TimeStamp, SHA1, MD5
Т.е. мы в XML будем писать имя файла, его размер в байтах, время и дату последней модификации файла и хеши SHA1 и MD5. Если при создании БД мы будем использовать только один хеш, то второй будет просто пустым. Для сборки XML нам потребуется потребуется функция. Эту схему можно разложить на 3 составные части:
Вот такая функция у меня получилась:
function _toxml_ { # в Begin создаём заголовок XML и открывающийся тег <FCIV> Begin { $xmlstring += "<?xml version=`"1.0`" ?>`n <FCIV>`n" } # в Process будут по одному поступать объекты, которые описывают файл. Process { # для каждого файла у нас будет один тег <FILE_ENTRY> $xmlstring += " <FILE_ENTRY>`n" # чтобы вручную не создавать и не заполнять вложенные теги мы простым foreach # перечисляем теги, какие у нас будут в XML и за счёт переменных автоматом # создаём их в XML и заполняем их данными. Для этого свойства объектов должны # называться так же, как и теги. foreach ($child in ("name", "Size", "TimeStamp", "SHA1", "MD5")) { $xmlstring += " <$child>$($_.$child)</$child>`n" } # когда текущий объект обработан, закрываем тег и ждём следующий объект файла $xmlstring += " </FILE_ENTRY>`n" } # когда объекты закончились, закрываем первый тег <FCIV> и подаём полученный XML дальше End { $xmlstring += "</FCIV>`n" $xmlstring } }
в ходе испытаний нарвался на одну засаду. В XML существуют некоторые символы как ‘&’, ‘<’, ‘>’ и другие (полный список перечислен тут: http://www.hdfgroup.org/HDF5/XML/xml_escape_chars.htm), которые нужно эксейпить, поскольку являются метасимволами в XML. Эти символы (в частности одинарная кавычка и амперсанд) могут присутствовать в имени файла. Погуляв по сайту MSDN нашёл выход. Выход заключается в использовании метода Escape класса System.SecurityElement. Поэтому после того как массив объектов (которые описывают каждый файл) будет собран его нужно пропустить через фильтр и только после этого собирать XML вышеприведённой функцией. Вот как выглядит фильтр:
filter _escxml_ { $_.name = [Security.SecurityElement]::Escape($_.name) $_ }
Напоминаю, что мы должны эскейпить только свойство Name нашего объекта.
Вот мы уже научились правильно создавать XML файл, который будет полностью совместим со схемой, которая используется в самом FCIV. Теперь у нас возникает задача чтения этого XML и преобразования в массив объектов. Сначала я долго горевал, плакал и бился головой об стену, что нельзя использовать Import-CliXML, но преобразование типов решило задачу ничуть не сложнее:
function _fromxml_ ($xml) { $sum = ([xml]$(gc -LiteralPath $xml)).FCIV.FILE_ENTRY | %{$_ | Select Name, Size, TimeStamp, SHA1, MD5} $sum }
сначала читаем файл и преобразовываем его в типа данных XML, опускаем теги FCIV и FILE_ENTRY и в цикле разбираем вложенные теги и собираем объект. Получилось достаточно просто. На сегодня, я думаю, что хватит, продолжение следует.
После продолжительного затишья вызванного различными причинами (как выступление на MCP клубе в Москве), продолжаю тему PowerShell. На этот раз реализованный на PowerShell аналог File Checksum Integrity Verifier (FCIV) с расширенным функционалом по просьбе Артёма Проничкина.
У каждого системного администратора имеется некоторый файловый ресурс, который содержит такие вещи как инсталляционные ресурсы для своей компании и зачастую эти ресурсы достаточно большие и очень важно отслеживать целостность каждого файла, что он не был случайно модифицирован dbhecjv или как-то иначе повреждён. А так же контролировать появление новых файлов и удаление имеющихся файлов. Так же при обмене файловыми ресурсами можно использовать данный скрипт (который будет опубликован чуть позже) для проверки целостности передачи данных по сети. Например, сформировали комплект файлов для пересылки по сети. Сначала заготавливается исходная БД и это всё передаётся по сети в точку назначения, где применяют этот же скрипт и приложенную БД и проверяют, все ли файлы пришли целыми.
Для решения этой задачи Microsoft в своё время выпустила утилиту FCIV - http://support.microsoft.com/kb/841290. Суть работы этой утилиты заключается в том, что она в указанной папке (и подпапках по желанию) подсчитывает хеш каждого файла и заносит его значение в свою БД. БД представляет собой обычный XML файл, который содержит имя и хеш-сумму для файла. При повторной проверки утилита считывает данные из XML файла, подсчитывает для каждого файла хеш и сравнивает его с тем, который записан в БД. Собственно, это весь функционал утилиты. Чтобы расширить функционал этой утилиты пришлось написать свой скрипт, который решает эту задачу. Причём этот скрипт будет полностью совместим с FCIV, хоть и использует свои механизмы подсчёта файлов и генерации XML файла.
Самое первое – правильно составить логику работы скрипта:
ищем XML
если XML есть
берём список файлов из XML
эталонные хеши тоже из XML
реальные хеши добываем при обсчёте файлов
сравниваем со всеми вытекающими последствиями
если файл есть в XML, а в папке нету - плачем
если XML нету
ничего не сравниваем
считаем хеши всех файлов в папке
записываем их в XML
Как оказалось, FCIV оказался непривередливым в отношении полей в XML файле, поэтому я решил добавить 2 своих поля – дата последней модификации и размер файла в байтах. Добавив 2 новых поля я позволил немного сократить общее время работы скрипта: как известно, подсчёт хешей для файлов процесс ресурсоёмкий (по CPU и по времени) и если мы при сравнении обнаружим, что изменились либо дата последней модификации, либо размер файла, то пересчитывать для него хеш совершенно не обязательно, поскольку есть 100% вероятность, что он не совпадёт с тем, что записан в БД. Итак, описание функционала скрипта:
Тело скрипта разбито на несколько функциональных частей:
Это была вводная часть в достаточно длительную эпопею с реализацией заданного функционала. В следующих частях в деталях будет рассмотрена каждая функциональная часть скрипта и в конце повествования будет опубликован сам скрипт, поэтому не отключайтесь.