##################################################################### # PsFCIV_1.0.ps1 # Version 1.0 # # File Checksum Integrity Verifier - PowerShell compatible version # # Note: requires PowerShell V2, not compatible with PowerShell 1.0 # Reference links: # http://support.microsoft.com/kb/841290 # http://www.sysadmins.lv/PermaLink,guid,22a8186c-615b-41fa-8062-d74e1a2a28e0.aspx # http://www.sysadmins.lv/PermaLink,guid,29898a6c-aceb-4738-82d8-318bc89272cc.aspx # # Vadims Podans (c) 2009 # http://www.sysadmins.lv/ ##################################################################### #requires -Version 2.0 function Start-PsFCIV { <# .Synopsis Checks files integrity. File Checksum Integreity Verifier (FCIV) compatible version. .Description This scipts calculates hashes for each file and stores this information in XML datafile. Since datafile is created script can check file integrity against this file. Script can use PsFCIV datafile format, FCIV datafile format and your own custom datafile format .Parameter Path Specifies the path to folder which will be verified by this sript .Parameter XML Specifies the path to the XML datafile that stores information about files. If file does not exist script will create it. this path can be absolute or relative. If relative then file must be placed in a root folder of Path parameter. .Parameter Include Specifies particular file to check. If specified, only this file will be checked during execution. .Parameter Action Specifies action to files with inconsistent length, modify date/time ar hash. Possible values are Rename or Delete. If specified Rename - script will add .BAD extension to file. .Parameter Show Specifies files that will be shown based on some criteria in graphic Out-GridView window. Possible values are: Total, New, Ok, Bad, Missed, Unknown Please note, to use this parameter you need .Net Framework 3.5 SP1 installed. .Parameter Recurse Specifies will the script check files in subfolders. .Parameter Rebuild Recreate XML datafile without checking files. If files from XML are missed in folder this switch will remove these entries from datafile. And if there are new files this switch will add entries for these files to datafile. .Parameter SHA1 Sets SHA1 hash algorithm for file check. Can be supplied with MD5 switch. .Parameter MD5 Sets MD5 hash algorythm for file check. Can be supplied with SHA1 switch. .Parameter Quiet When script finish job, it will exit PowerShell session with numeric exit code. Exit codes described in Outputs section .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -XML DB.XML -SHA1 This will check all files in C:\tmp folder using SHA1 hash algorithm. .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -XML DB.XML -SHA1 -MD5 -Recurse This will check all files in C:\tmp folder and subfolders using both SHA1 and MD5 hash algorithms. .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -Include *.txt -XML DB.XML -SHA1 -MD5 This will check all TXT files in C:\tmp folder using both SHA1 and hash algorithms. .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -XML DB.XML -SHA1 -Rebuild This will rebuild DB file, removing all unused entries (when entry exist, but file - not) from XML file and add all new files that hasn't records in XML file using SHA1 algorithm. Existing files will not checked for integrity consistence. .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -XML DB.XML -MD5 -Action Rename This will check all files in C:\tmp folder using MD5 algorithm and rename files with Length, LastWriteTime or hash mismatch adding to them .BAD extension. Here may be 'Delete' action, that will delete all bad files. .EXAMPLE PS > Start-PsFCIV -Path C:\tmp -XML DB.XML -SHA1 -Show Ok, Bad This will check all files in C:\tmp folder using SHA1 algorithm and will show filenames that matches Ok or Bad category. .Outputs Script can return different output depending from -Quiet switch. If switch is passed script doesn't return anything to window, but generate ExitCode depending on file check results. Here is a list of possible exitcodes: 0 - all files are ok 1 - here is some bad files 2 - here is some missing files 3 - here is some files with Unknown status 4 - here is some locked and unchecked files 5 - Rebuild mode if -Quiet switch is not present, then script generates general statistics about checked files, such total processed files count, total files with Good, Bad, Locked, Missed, New, Unnown status. .Link http://support.microsoft.com/kb/841290 .Link http://www.sysadmins.lv/PermaLink,guid,22a8186c-615b-41fa-8062-d74e1a2a28e0.aspx .Link http://www.sysadmins.lv/PermaLink,guid,29898a6c-aceb-4738-82d8-318bc89272cc.aspx #> [CmdletBinding()] param ( # объявляем нужные переменные [Parameter(Mandatory = $true, Position = 0)] [string]$Path, [Parameter(Mandatory = $true, Position = 1)] [string]$XML, # переменная $Include может проверить либо все файлы, либо один конкретный файл [Parameter(Position = 2)] [string]$Include = "*.*", # этим параметром задаём уровень вывода Brief или Full # ключом Force мы можем задавать действие для проблемных файлов: переименовать или удалить [string][ValidateSet("Rename", "Delete")]$Action, # переменная $show должна содержать имена всех файлов, разбитых по категориям, чтобы # иметь возможность посмотреть, например, пропущенные, удалённые, добавленные, # с несоответствющим хешем файлы [string[]][ValidateCount(1,7)][ValidateSet("Bad", "Locked", "Missed", "New", "Ok", "Total", "Unknown")]$Show, # проверяем либо текущую папку, либо подпапки тоже [switch]$Recurse, # задаёт особый режим работы, когда файл БД очищается от устаревших записей и в неё # добавляются новые файлы, которые были созданы после создания БД [switch]$Rebuild, [switch]$SHA1, [switch]$MD5, [switch]$Quiet ) # первым делом проверяем, что указан хотя бы один алгоритм хеширования. Если не указано # ни одного, то начинаем очень ругаться и останавливаем работу скрипта if (!$sha1 -and !$md5) { throw "You must specify at least one hash algorythm." } # задаём действие при использовании ключей -Verbose и -Debug if ($PSBoundParameters.Verbose) {$VerbosePreference = "continue"} if ($PSBoundParameters.Debug) {$DebugPreference = "continue"} $oldverb = $host.PrivateData.VerboseForegroundColor $olddeb = $host.PrivateData.DebugForegroundColor # резервируем текущий путь, чтобы на него вернуться после окончания работы $oldpath = $pwd.Path # проверяем, что нам задали валидный путь для проверки. Если путь существует, то # устанавливаем текущий путь на $path, в противном случае начинаем ругаться if (Test-Path -LiteralPath $path) { Set-Location -LiteralPath $path if ($pwd.Provider.Name -ne "FileSystem") { Set-Location $oldpath throw "Specified path is not filesystem path. Try again!" } } else { throw "Specified path not found. Try again!" } # добавлены в статистических целях $sfile = $null $sum = $new = @() # создаём объект статистики, который в каждом свойстве будет хранить файлы # соответствующей категории $global:stats = "" | select "Total", "New", "Ok", "Bad", "Missed", "Locked", "Unknown" # поскольку в свойствах будут объекты, то их объявляем массивами $global:stats | gm -MemberType NoteProperty | %{$global:stats.$($_.name) = @()} $global:stats | Add-Member NoteProperty Del ([int]0) # функция враппер командлета Get-ChildItem function dirx ([string]$path, [string]$Filter, [string[]]$Exclude, $Recurse, [switch]$Force) { dir @PsBoundParameters | ?{!$_.psiscontainer} } # функция определения и исключения заблокированных файлов. Заблокированные файлы # будут относиться к категории Unknown function _filelock_ ($file) { $locked = $false trap {Set-Variable -name locked -value $true -scope 1; continue} $inputStream = New-Object System.IO.StreamReader $file if ($inputStream) {$inputStream.Close()} if ($locked) { $host.PrivateData.VerboseForegroundColor = "Yellow" $host.PrivateData.DebugForegroundColor = "Yellow" Write-Verbose "File $filename is locked. Skipping this file.." Write-Debug "File $filename is locked. Skipping this file.." $stats.Locked += $file.Name $stats.Total += $file.Name } $locked } # функция для создания графического вывода списка файлов по категориям с # использованием Out-GridView function _formatter_ ($show, $max) { $total = @($input) # мы сначала берём те свойства, которые были выбраны при вызове скрипта foreach ($property in $show) { # и циклом их разбираем в читабельный вид и выводим в виде графических окон $(for ($n = 0; $n -lt $max; $n++) { $total[0] | select @{n=$property;e={$_.$property[$n]}} }) | Out-GridView -Title $property } } # основная функция подсчёта файлов. На выходе даст массив байтов, который мы преобразуем # в base64 строку 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 } function _toxml_ { # в Begin создаём заголовок XML и открывающийся тег Begin { $xmlstring = New-Object System.Text.StringBuilder [void]$xmlstring.Append("`n `n") } # в Process будут по одному поступать объекты, которые описывают файл. Process { # для каждого файла у нас будет один тег [void]$xmlstring.Append(" `n") # чтобы вручную не создавать и не заполнять вложенные теги мы простым foreach # перечисляем теги, какие у нас будут в XML и за счёт переменных автоматом # создаём их в XML и заполняем их данными. Для этого свойства объектов должны # называться так же, как и теги. foreach ($child in ("name", "Size", "TimeStamp", "SHA1", "MD5")) { [void]$xmlstring.Append(" <$child>$($_.$child)`n") } # когда текущий объект обработан, закрываем тег и ждём следующий объект файла [void]$xmlstring.Append(" `n") } # когда объекты закончились, закрываем первый тег и подаём полученный XML дальше End { [void]$xmlstring.Append("`n") [string]$xmlstring.ToString() } } function _fromxml_ ($xml) { # читаем XML файл и собираем из него массив объектов $sum = ([xml]$(gc -LiteralPath $xml)).FCIV.FILE_ENTRY | %{$_ | Select Name, Size, TimeStamp, SHA1, MD5} $sum } function _makeobject_ ($sha1, $md5, $filename, $file) { $host.PrivateData.DebugForegroundColor = "Yellow" Write-Debug "Starting object creation for $filename ..." # вот тут собственно происходит формирование объекта, который будет описывать конкретный файл $object = "" | Select Name, Size, TimeStamp, SHA1, MD5 $object.name = [Security.SecurityElement]::Escape($filename) $object.Size = $file.length $object.TimeStamp = ($file.lastwritetime).ToString() # здесь автоматом считаем нужные хеши и на лету конвертируем их в base64 строку if ($sha1) { Write-Debug "Calculating SHA1 hash..." $object.SHA1 = [System.Convert]::ToBase64String($(_hashbytes_ "sha1" $(Join-Path $pwd.providerpath $filename))) } if ($md5) { Write-Debug "Calculating MD5 hash..." $object.MD5 = [System.Convert]::ToBase64String($(_hashbytes_ "md5" $(Join-Path $pwd.providerpath $filename))) } Write-Debug "Object created!" $object } # простая функция, которая будет подсчитывать хеш для проверямого файла и собирать его # в hex-строку вида B926D7416E8235E6F94F756E9F3AE2F33A92B2C4. function _precheck_ ($sha1, $md5, $filename) { $host.PrivateData.DebugForegroundColor = "Yellow" if ($sha1) { Write-Debug "Calculating SHA1 hash..." -join ($(_hashbytes_ "sha1" $(Join-Path $pwd.providerpath $filename)) | %{"{0:X2}" -f $_}) } elseif ($md5) { Write-Debug "Calculating MD5 hash..." -join ($(_hashbytes_ "md5" $(Join-Path $pwd.providerpath $filename)) | %{"{0:X2}" -f $_}) } } function _takeaction_ ($Action, $filename) { # мы можем задать на уровне скрипта что делать с такими файлами. Действует только # при указании ключа -Action. Мы можем переименовывать такие файлы или удалять автоматически switch ($Action) { "Rename" {Ren $filename $($filename + ".bad") -Verbose} "Delete" {Del $filename -Force -Verbose} } } # стержень скрипта - основная рутинная проверка соответствия файла записям в XML файле function _checkfiles_ ($filename, $hexhash, $sum, $Action, $stats) { # из полученных аргументов берём имя, собираем путь и убеждаемся, что такой файл существует if (Test-Path -LiteralPath $(Join-Path $pwd.providerpath $filename)) { # через Get-Item выбираем объект файла и снова выбираем запись из XML файла для сравнения $current = $sum | ?{$_.name -eq $filename} $file = gi -LiteralPath $filename -Force if ($current) { # если соответствующая запись найдена, то сравниваем размер и дату/время последней модификации # файла с записью в XML файле if (($file.length -eq $current.size) -and ($file.lastwritetime.ToString() -eq $current.TimeStamp)) { # если эти параметры совпали, то проверяем хеш. При этом убеждаемся, что в XML записи # хранится тот тип хеша, который указан в аргументах вызова скрипта if ($current.SHA1 -and $sha1) { # если совпадение есть то конвертируем base64 строку в hex строку $ActualHash = -join ([system.convert]::FromBase64String($current.SHA1) | %{"{0:X2}" -f $_}) } elseif ($current.MD5 -and $md5) { $ActualHash = -join ([system.convert]::FromBase64String($current.MD5) | %{"{0:X2}" -f $_}) } else { # если совпадений не обнаружено, т.е. в аргументах скрипта указан -sha1 ключ, а # XML запись хранит только MD5 хеш, то мы выдаём ошибку, что для данного файла # нету сохранённого подходящего хеша $host.PrivateData.VerboseForegroundColor = "Red" Write-Verbose "Cannot bind -sha1 or -md5 switch to containing hash for file: $file.name" $global:stats.Unknown += $filename return } # если же у нас есть нужные записи, то сверяем посчитанный хеш с записанным в XML хешем if ($ActualHash -eq $hexhash) { # в зависимости от указанного уровня вывода мы можем выводить информацию на экран # и добавляем в счётчики сведения о проверке данного файла $host.PrivateData.VerboseForegroundColor = "Green" Write-Verbose "$filename - ok" $global:stats.Ok += $filename $global:stats.Total += $filename return } else { # если размер и дата последней модификации файла и данные из XML записи совпадают, # но не совпал хеш, то пополняем нужный счётчик сведениями о проверке и выводим # на экран информацию, что хеш не совпал. При этом в зависимости от уровня вывода на экран # (ключ -Verbose) мы можем просто информировать или выводить актуальный хеш файла # и тот хеш, что записан в XML для данного файла $global:stats.Bad += $filename $host.PrivateData.VerboseForegroundColor = "Red" Write-Verbose "$filename - bad hash" $host.PrivateData.DebugForegroundColor = "Red" Write-Debug "$filename - bad hash Expected hash: $hexhash Actual hash: $ActualHash" if ($Action) {_takeaction_ $Action $filename} } } else { # если проверка на размер или дату/время модификации файла, то выводим сообщение на экран # и пополняем счётчик $global:stats.Bad += $filename $host.PrivateData.VerboseForegroundColor = "Red" Write-Verbose "File $file.name size or Modified Date/Time mismatch!" if ($Action) {_takeaction_ $Action $filename} } } } else { # если для соответствующей записи в XML не найден реальный файл, то так же выводим сообщение, # что файл удалён (хотя он может быть просто переименован) $host.PrivateData.VerboseForegroundColor = "Yellow" Write-Verbose "$filename - missing" $global:stats.Missed += $filename $global:sum return } # подбиваем статистику и переходим к следующей итерации проверки файла $global:stats.Total += $filename $global:sum } function stats ($sum, $xml, $stats, $Quiet) { # если указан ключ -Show, то мы можем посмотреть список файлов, которые подпали # под одну из категорий (Total, New, Ok, Bad, Missed, Unknown. Можно указать несколько # аргументов этого параметра через запятую if ($show) { $stats | select $show | _formatter_ $show $stats.Total.Count } # ну и сама итоговая статистка работы скрипта в цифрах if (!$Quiet) { Write-Host ----------------------------------- -ForegroundColor Green if ($Rebuild) { Write-Host Total entries processed: $stats.Total.Count -ForegroundColor Cyan Write-Host Total removed unused files: $stats.Del -ForegroundColor Yellow } else {Write-Host Total files processed: $stats.Total.Count -ForegroundColor Cyan} Write-Host Total new added files: $stats.New.Count -ForegroundColor Green Write-Host Total good files: $stats.Ok.Count -ForegroundColor Green Write-Host Total bad files: $stats.Bad.Count -ForegroundColor Red Write-Host Total unknown status files: $stats.Unknown.Count -ForegroundColor Yellow Write-Host Total missing files: $stats.Missed.Count -ForegroundColor Yellow Write-Host Total locked files: $stats.Locked.Count -ForegroundColor Yellow Write-Host ----------------------------------- -ForegroundColor Green } # возвращаемся на исходный путь и возвращаем значения служебных переменных, # которые были изменены в ходе работы скрипта Set-Location -LiteralPath $oldpath $host.PrivateData.VerboseForegroundColor = $oldverb $host.PrivateData.DebugForegroundColor = $olddeb # создаём код возврата if ($Rebuild) {$exit = 5} else { if ($stats.Bad.Count -ne 0) {$exit = 1} elseif ($stats.Missed.Count -ne 0) {$exit = 2} elseif ($stats.Unknown -ne 0) {$exit = 3} elseif ($stats.Locked -ne 0) {$exit = 4} else {$exit = 0} } if ($Quiet) {exit $exit} } # для освежения файла БД, т.е. удаления устаревших файлов и добавления записей для новых # файлов в папках, которые не отражены в БД, введён специальный режим Rebuild, который # включается по соответствующему ключу -Rebuild. При этом режиме работы производится только # проверка, что файл из записи существует на диске. Если такого файла нету, то запись удаляется # из XML. После чего производится поиск новых файлов и для них создаются соответствующие объекты # которые в последствии записываются в обновлённый XML файл. if ($rebuild) { $host.PrivateData.DebugForegroundColor = "White" Write-Debug "Rebuild mode ON" if (Test-Path -LiteralPath $xml) { $old += _fromxml_ $xml } else { Set-Location $oldpath throw "Unable to find XML file. Please, run script without '-Rebuild' switch" } [void]($interm = $old | ?{[bool](Test-Path -LiteralPath $_.name) -and $_.name -ne $xml}) $stats.Del = $old.count - $interm.count dirx -path .\ -filter $Include -exclude $xml $Recurse -force | %{ $file = gi -LiteralPath $_.fullname -Force if (_filelock_ $file) {return} $filename = [regex]::Escape($($pwd.providerpath + "\")) $filename = $file.FullName -replace $filename $host.PrivateData.VerboseForegroundColor = "Green" if ($interm | ?{$_.name -eq $filename}) { Write-Verbose "File $filename is ok" return } else { Write-Verbose "File $filename added" $new += _makeobject_ $sha1 $md5 $filename $file $stats.New += $filename $stats.Total += $filename } } $interm = $interm | %{$_.name = [Security.SecurityElement]::Escape($_.name); $_} $newest = $interm + $new if ($newest.count -eq 0) { $host.PrivateData.VerboseForegroundColor = "Yellow" $host.PrivateData.DebugForegroundColor = "Yellow" Write-Verbose "Here is no data to write to XML database." Write-Debug "Here is no data to write to XML database." } else { $host.PrivateData.DebugForegroundColor = "Cyan" Write-Debug "Preparing to DataBase file creation..." $newest | _toxml_ > $xml Write-Debug "DataBase file created..." } stats $newest $xml $stats Set-Location $oldpath return } # все необходимые функции готовы, теперь можно приступать к самой рутине, т.е. # начать с чтения XML файла if (Test-Path -LiteralPath $xml) { $sum += _fromxml_ $xml # проверяем входной XML. Если этот XML сгенерирован утилитой FCIV, то свойства Size # у него не будет. Поэтому создаём обычный объект для текущего файла с подсчётом хеша. # в качестве эталонного хеша берём именно хеш из XML файла. if ($sum[0].size -eq $null) { $host.PrivateData.DebugForegroundColor = "White" Write-Debug "FCIV mode ON" $sum | %{ $filename = $_.name # сначала удаляем из переменной запись о файле в формате FCIV, т.к. # здесь не хватает сведений для проверки файла. Из этой записи мы # будем использовать только хеши в операции сравнения файла с записью $sum = @($sum | ?{$_.name -ne $filename}) # если файла не оказалось, то вычёркиваем его из обработки if (Test-Path -LiteralPath $filename) { $host.PrivateData.VerboseForegroundColor = "Yellow" Write-Verbose "File $filename not found. Skipped" $stats.Missed += $filename return } $file = gi -LiteralPath $filename -Force -ea 0 if (_filelock_ $filename) {return} $hexhash = _precheck_ $sha1 $md5 $filename # теперь добавляем уже новый объект файла, который уже годен для сравнения $sum += @(_makeobject_ $sha1 $md5 $filename $file) [void](_checkfiles_ $filename $hexhash $sum $Action) # если файл не прошёл проверку по хешу, то данные о нём не записываем в новый # XML. if ($stats.ok -notcontains $filename) {$sum = @($sum | ?{$_.name -ne $filename})} } if ($sum.count -eq 0) { $host.PrivateData.VerboseForegroundColor = "Yellow" $host.PrivateData.DebugForegroundColor = "Yellow" Write-Verbose "Here is no data to write to XML database." Write-Debug "Here is no data to write to XML database." } else { $host.PrivateData.DebugForegroundColor = "Cyan" Write-Debug "Preparing to DataBase file creation..." $sum | _toxml_ > $xml Write-Debug "DataBase file created..." } stats $sum $xml $stats $Verbose $Debug Set-Location $oldpath return } } # эта часть кода предназначена для интеграции с другим форматом # БД, на основании которой этот скрипт будет проверять файлы if (Test-Path CRC -PathType Container) {$crc = "crc"} else {$crc = "."} if (Test-Path $($crc + "\" + "*.sha1.txt")) { $host.PrivateData.DebugForegroundColor = "White" Write-Debug "Alternate source mode ON" $sha1 = $true dirx -path $crc -filter ($Include + ".sha1.txt") -force | %{ [void]($_.name -match "(.*).sha1.txt") $filename = $matches[1] $sum = @($sum | ?{$_.name -ne $filename}) $file = gi -LiteralPath $filename -Force -ea 0 if (_filelock_ $filename) {return} if (!$file) { $host.PrivateData.VerboseForegroundColor = "Yellow" Write-Verbose "$filename - missing" $global:stats.Missed += $filename return } [void]($(gc $($crc + "\" +$matches[0])) -match "..(.{40})") $hexhash = $matches[1] $sum += @(_makeobject_ $sha1 -filename $filename -file $file) [void](_checkfiles_ $filename $hexhash $sum $Action) } if ($sum.count -eq 0) { $host.PrivateData.VerboseForegroundColor = "Yellow" $host.PrivateData.DebugForegroundColor = "Yellow" Write-Verbose "Here is no data to write to XML database." Write-Debug "Here is no data to write to XML database." } else { $host.PrivateData.DebugForegroundColor = "Cyan" Write-Debug "Preparing to DataBase file creation..." $sum | _toxml_ > $xml Write-Debug "DataBase file created..." } stats $sum $xml $stats $Verbose $Debug } else { # если XML файл есть, то новые записи в него вноситься не будут, а будет происходить только # сверка БД с действительными файлами if ($sum) { $host.PrivateData.DebugForegroundColor = "White" Write-Debug "XML mode ON" # эта часть выполняется только если мы хотим указать или проверить только конкретный файл if ($Include -ne "*.*") { $sfile = $sum | ?{$_.name -eq $Include -and !$_.psiscontainer} $filename = $Include # если указанный файл существует. то считаем для него хеш if (Test-Path -LiteralPath $(Join-Path $pwd.providerpath $filename)) { $hexhash = _precheck_ $sha1 $md5 $filename # и отправляем на стандартную рутину проверки файла. [void](_checkfiles_ $filename $hexhash $sum $Action) # и готовим итоговый вывод на экран консоли stats $sum $xml $stats $Verbose $Debug return } else { # если файл для этой записи не существует, то пропускаем его, выводя соответствующее сообщение на экран $host.PrivateData.VerboseForegroundColor = "Yellow" Write-Verbose "File $filename not found. Skipped" $stats.Missed += $filename } } else { # мы можем проверять файлы как только в текущей папке (по умолчанию), так и во вложенных # папках тоже. Т.к. все имена файлов во вложенных папках будут содержать слеши, то # при отсутствии ключа -Recurse мы исключаем такие файлы. И дальше повторяем проверку # как и в случае с одиночным файлом. if ($Recurse) {$xsum = $sum} else {$xsum = $sum | ?{$_.name -notmatch "\\"}} $xsum | %{$filename = $_.name if (Test-Path -LiteralPath $(Join-Path $pwd.providerpath $filename)) { $hexhash = _precheck_ $sha1 $md5 $filename [void](_checkfiles_ $filename $hexhash $sum $Action) } else { $host.PrivateData.VerboseForegroundColor = "Yellow" Write-Verbose "File $filename not found. Skipped" $stats.Missed += $filename } } } } else { # если XML файла нету, то мы будем создавать новую БД с записями $host.PrivateData.DebugForegroundColor = "White" Write-Debug "New XML mode ON" dirx -path .\ -filter $Include -exclude "." $Recurse -force | %{ $host.PrivateData.VerboseForegroundColor = $host.UI.RawUI.ForegroundColor Write-Verbose "Perform file $filename checking" $file = gi -LiteralPath $_.fullname -Force # у имени файла отрезаем текущий путь и оставляем только хвост $filename = [regex]::Escape($($pwd.providerpath + "\")) $filename = $file.FullName -replace $filename if (_filelock_ $file) {return} # собираем объект XML записи для файла и добавляем её в массив $sum += _makeobject_ $sha1 $md5 $filename $file $stats.New += $filename $stats.Total += $filename } # когда массив файлов будет собран - подаём его на запись в файл if ($sum.count -eq 0) { $host.PrivateData.VerboseForegroundColor = "Yellow" $host.PrivateData.DebugForegroundColor = "Yellow" Write-Verbose "Here is no data to write to XML database." Write-Debug "Here is no data to write to XML database." } else { $host.PrivateData.DebugForegroundColor = "Cyan" Write-Debug "Preparing to DataBase file creation..." $sum | _toxml_ > $xml Write-Debug "DataBase file created..." } } stats $sum $xml $stats $Verbose $Debug } } # SIG # Begin signature block # MIIQNAYJKoZIhvcNAQcCoIIQJTCCECECAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUKA6hhaYJA3h5nQLA8AC5S7b4 # QmSgggykMIIDejCCAmKgAwIBAgIQOCXX+vhhr570kOcmtdZa1TANBgkqhkiG9w0B # AQUFADBTMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xKzAp # BgNVBAMTIlZlcmlTaWduIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EwHhcNMDcw # NjE1MDAwMDAwWhcNMTIwNjE0MjM1OTU5WjBcMQswCQYDVQQGEwJVUzEXMBUGA1UE # ChMOVmVyaVNpZ24sIEluYy4xNDAyBgNVBAMTK1ZlcmlTaWduIFRpbWUgU3RhbXBp # bmcgU2VydmljZXMgU2lnbmVyIC0gRzIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ # AoGBAMS18lIVvIiGYCkWSlsvS5Frh5HzNVRYNerRNl5iTVJRNHHCe2YdicjdKsRq # CvY32Zh0kfaSrrC1dpbxqUpjRUcuawuSTksrjO5YSovUB+QaLPiCqljZzULzLcB1 # 3o2rx44dmmxMCJUe3tvvZ+FywknCnmA84eK+FqNjeGkUe60tAgMBAAGjgcQwgcEw # NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC52ZXJpc2ln # bi5jb20wDAYDVR0TAQH/BAIwADAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js # LnZlcmlzaWduLmNvbS90c3MtY2EuY3JsMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMI # MA4GA1UdDwEB/wQEAwIGwDAeBgNVHREEFzAVpBMwETEPMA0GA1UEAxMGVFNBMS0y # MA0GCSqGSIb3DQEBBQUAA4IBAQBQxUvIJIDf5A0kwt4asaECoaaCLQyDFYE3CoIO # LLBaF2G12AX+iNvxkZGzVhpApuuSvjg5sHU2dDqYT+Q3upmJypVCHbC5x6CNV+D6 # 1WQEQjVOAdEzohfITaonx/LhhkwCOE2DeMb8U+Dr4AaH3aSWnl4MmOKlvr+ChcNg # 4d+tKNjHpUtk2scbW72sOQjVOCKhM4sviprrvAchP0RBCQe1ZRwkvEjTRIDroc/J # ArQUz1THFqOAXPl5Pl1yfYgXnixDospTzn099io6uE+UAKVtCoNd+V5T9BizVw9w # w/v1rZWgDhfexBaAYMkPK26GBPHr9Hgn0QXF7jRbXrlJMvIzMIIDxDCCAy2gAwIB # AgIQR78Zld+NUkZD99ttSA0xpDANBgkqhkiG9w0BAQUFADCBizELMAkGA1UEBhMC # WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUx # DzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x # HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNMDMxMjA0MDAwMDAw # WhcNMTMxMjAzMjM1OTU5WjBTMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNp # Z24sIEluYy4xKzApBgNVBAMTIlZlcmlTaWduIFRpbWUgU3RhbXBpbmcgU2Vydmlj # ZXMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpyrKkzM0grwp9 # iayHdfC0TvHfwQ+/Z2G9o2Qc2rv5yjOrhDCJWH6M22vdNp4Pv9HsePJ3pn5vPL+T # rw26aPRslMq9Ui2rSD31ttVdXxsCn/ovax6k96OaphrIAuF/TFLjDmDsQBx+uQ3e # P8e034e9X3pqMS4DmYETqEcgzjFzDVctzXg0M5USmRK53mgvqubjwoqMKsOLIYdm # vYNYV291vzyqJoddyhAVPJ+E6lTBCm7E/sVK3bkHEZcifNs+J9EeeOyfMcnx5iIZ # 28SzR0OaGl+gHpDkXvXufPF9q2IBj/VNC97QIlaolc2uiHau7roN8+RN2aD7aKCu # FDuzh8G7AgMBAAGjgdswgdgwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBBBgNV # HR8EOjA4MDagNKAyhjBodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9UaGF3dGVUaW1l # c3RhbXBpbmdDQS5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgEGMCQGA1UdEQQdMBukGTAXMRUwEwYDVQQDEwxUU0EyMDQ4LTEtNTMwDQYJKoZI # hvcNAQEFBQADgYEASmv56ljCRBwxiXmZK5a/gqwB1hxMzbCKWG7fCCmjXsjKkxPn # BFIN70cnLwA4sOTJk06a1CJiFfc/NyFPcDGA8Ys4h7Po6JcA/s9Vlk4k0qknTnqu # t2FB8yrO58nZXt27K4U+tZ212eFX/760xX71zwye8Jf+K9M7UhsbOCf3P0owggVa # MIIDQqADAgECAgoWkwfVAAAAAAATMA0GCSqGSIb3DQEBBQUAMEkxEjAQBgoJkiaJ # k/IsZAEZFgJsdjEZMBcGCgmSJomT8ixkARkWCXN5c2FkbWluczEYMBYGA1UEAxMP # c3lzYWRtaW5zLUxWLUNBMB4XDTA5MDgwNzEzMzMyOVoXDTEwMDgwNzEzMzMyOVow # VzESMBAGCgmSJomT8ixkARkWAmx2MRkwFwYKCZImiZPyLGQBGRYJc3lzYWRtaW5z # MQ4wDAYDVQQDEwVVc2VyczEWMBQGA1UEAxMNQWRtaW5pc3RyYXRvcjCBnzANBgkq # hkiG9w0BAQEFAAOBjQAwgYkCgYEAloTUoXflQDFR9ZS5sAdOT1QKQQ5IKCpaY2Vv # c9Lxlymo2VI0T3f8lBpzVm2C6ZeKtSyHs+GjbklFUDFDgT8wJLHgE5aFhFukh7nh # wJQsfNKyg10zE/mebfIZIGZ9IPFGDvMVJW+eV+skUjBn58tDFR+3IgnjkOGqbd6R # 8Dsir28CAwEAAaOCAbgwggG0MA4GA1UdDwEB/wQEAwIHgDA7BgkrBgEEAYI3FQcE # LjAsBiQrBgEEAYI3FQiGmb0x4rBigu2DJtLefoO13VFhhLz2C4T0qhkCAWQCAQIw # HQYDVR0OBBYEFFVXJ/UEPfMmoCnu5WNbREsdwlerMB8GA1UdIwQYMBaAFHrmiNrp # +Fw+BwTeEaI4Iql1oxOrMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jYS5zeXNh # ZG1pbnMubHYvc3lzYWRtaW5zLUxWLUNBLmNybDB/BggrBgEFBQcBAQRzMHEwJwYI # KwYBBQUHMAGGG2h0dHA6Ly9jYS5zeXNhZG1pbnMubHYvb2NzcDBGBggrBgEFBQcw # AoY6aHR0cDovL2NhLnN5c2FkbWlucy5sdi9jYS5zeXNhZG1pbnMubHZfc3lzYWRt # aW5zLUxWLUNBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDAzAbBgkrBgEEAYI3FQoE # DjAMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCygKgYKKwYBBAGCNxQCA6AcDBpBZG1p # bmlzdHJhdG9yQHN5c2FkbWlucy5sdjANBgkqhkiG9w0BAQUFAAOCAgEAiAoSViW5 # /n4EJZmLBGrFPoTOCPcxkuCeCm8aLDjvvKZZWl0ylZFrCf099NmjdsBZJjk7Gvd6 # NhiCX80QJaZvoGv/dTK9EvqlFKO46aQzIPrEVCX7FmEA5yDSvmyO/lFsa5m5oM5x # Q6otTM6ZooGNLBMYmaFkJ6N9OsgR1x9A+2f6htAH5BwdmacnvYwMTl3SNmR9rvxJ # HALAErVBWFkwOlU7FUnLQSmlKVhXKaDhrp/mm3ETloRWPVm7jkqLwOnFCys1dTBx # dIDGNHjboWFvUF4zJSJ/vsQhw0aF8bTSP0UIkh0689bKw+ae0+Z62gtVq4usOJMF # zANhgtLcNYUlsB46AIgenneeK0aZAgQ6jGXV0BTWrzMntF6jv7/cA/lhp3AG5Bg8 # hjR890YrrLk6l65zUnJlesnW45hg0wmgb5DDceaXiVJN1Gb9GhbdQfd9nHqCXQnl # hU4eTVyGGfvwaO0qGlklf/aCYKwTThrWLYTrXCbggGkZJqldj8uTAz5fwRgjZ6fV # 9JbMz6bgFdWiOpJUEd30R56i2QNVnu0+EtueTuV26NLbqis2Wi4pbS9jqO44pFMa # usPpPqsu5xoQ8Ad8Wb5JOMA/PClSf7OFXPeqyHgWHZHwMKXY5GdUbLLU2ZfifK1u # c55wnZ+kPGm5r3BUY2CVhvltZIJw6r7nTOsxggL6MIIC9gIBATBXMEkxEjAQBgoJ # kiaJk/IsZAEZFgJsdjEZMBcGCgmSJomT8ixkARkWCXN5c2FkbWluczEYMBYGA1UE # AxMPc3lzYWRtaW5zLUxWLUNBAgoWkwfVAAAAAAATMAkGBSsOAwIaBQCgeDAYBgor # BgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE # MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBTk # GhbCgolBKOlDwIgTRyzyjl0j4TANBgkqhkiG9w0BAQEFAASBgE4AaIReWhMDkMDF # lR0sC0sQhAE8WsIZ4yJ3ZGGasWfzKQsk+NvRH5dzobB8LssJnILHfF7WfgFRM4gc # wJQMm7M3ik6Srn+EaFffqPoybJztXEUnDDd/sYyDbVf3IldRJpaGkuhIrpOab0Qt # LhHaD718iJrWnDXdMDuXDaVDC3/FoYIBfzCCAXsGCSqGSIb3DQEJBjGCAWwwggFo # AgEBMGcwUzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMSsw # KQYDVQQDEyJWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIENBAhA4Jdf6 # +GGvnvSQ5ya11lrVMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN # AQcBMBwGCSqGSIb3DQEJBTEPFw0wOTEwMDExODAxMDFaMCMGCSqGSIb3DQEJBDEW # BBQkOEGNJ7ZedX93LqZFYRQo9IXq8zANBgkqhkiG9w0BAQEFAASBgKe3Cuaa4hEP # ZpbmKLmB88XN3FmbMD1xhweeTz81GP/J3DKP17MSxn8imvtBbbudr2gkY8/T5DEm # IFsHR+Vit3PjYh7ixFG4aKJ+y0v5aur7g8DkMgV0Ie57D3ufDXFjQalNMxoRBPeA # TAuZ/RGocQrnjgz9SYR0/+5JvsnIXagZ # SIG # End signature block