Pull to refresh

Почему DFSR не реплицирует некоторые файлы и как с этим бороться

Reading time4 min
Views6K

Как многим известно, в свойствах реплицируемых папок можно настроить исключения в виде масок файлов — и тогда служба не будет реплицировать файлы, соответствующие заданным маскам. Но не все знают, что у файлов есть атрибут «временный», и DFSR не обрабатывает такие файлы by design. И если это не учесть, то может случиться так, что содержимое ваших DFSR-папок станет рассинхронизированным, хотя в логах службы всё будет чисто и красиво, и всплыть это может в самый неподходящий момент. Сама проблема и ее решение уже не раз разбирались в интернете, цель же этой статьи — доработать созданное ранее решение, добавив ему гибкости и удобства. Для кого актуально — прошу под кат.

Тема давно и не раз освещалась в интернете, например в официальном блоге, тут и на самом Хабре. Поэтому не буду повторяться, а перейду сразу к делу. Исходное решение представляет собой скрипт на PowerShell, который сбрасывает атрибут «временный» для всех файлов в заданной папке. Взяв его за основу, я написал свой вариант, который находит реплицируемые папки на целевом сервере и пробегается по каждой из них, сбрасывая проблемный атрибут, после чего отправляет отчет о найденных файлах на email. Также добавлена поддержка длинных путей (необходима 5-я версия PowerShell).

Для работы скрипту нужны инструменты управления службами DFS (по умолчанию устанавливаются вместе с добавлением роли DFS Replication). Если же на целевом сервере они отсутствуют, запустите этот командлет:

Install-WindowsFeature RSAT-DFS-Mgmt-Con

А вот сам скрипт:

TempAttrFixer.ps1
Param(
    #Куда сохраняем отчеты
    [parameter(Mandatory=$false)][String]$OutDir
)

$SMTPServer = "mail.mydomain.com"
$MailFrom = "sender@mydomain.com"
$MailTo = "recipient@mydomain.com"

Function ConvertTo-LiteralPath {
    #Преобразуем формат пути из обычного в LiteralPath
    Param(
        [parameter(Mandatory=$true)][String]$Path
    )
    #Если путь в формате UNC
    If ($Path.Substring(0,2) -eq "\\") {
        Return ("\\?\UNC" + $Path.Remove(0,1))
    }
    Else {
        Return "\\?\$Path"
    }
}

$StartTime = Get-Date

$Error.Clear()

If (!$OutDir) {
    $OutDir = (Get-WmiObject Win32_OperatingSystem).SystemDrive +"\TempAttrFixer_Report"
}

#Получаем список реплицируемых папок, расположенных на сервере
$FoldersToScan = @(Get-DfsrMembership -ComputerName $env:COMPUTERNAME | Sort-Object GroupName, FolderName).ContentPath

$LogFileName = "$env:COMPUTERNAME" + "_TempFiles_" + (Get-Date -Format "yyyy-MM-dd-HH-mm-ss") + ".csv"
$LogFilePath = "$OutDir\$LogFileName"
$Delimiter = "`t"
$FilesCount = 0
If (!(Test-Path $OutDir -PathType Container)) {
    New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
}

ForEach ($Folder in $FoldersToScan) {
    #Нужен PowerShell 5.1, иначе командлет не поймет аргумент LireralPath
    Write-Output "Scanning `"$Folder`"..."
    Get-ChildItem -LiteralPath (ConvertTo-LiteralPath $Folder) -Recurse | ForEach-Object -Process {
        if (($_.Attributes -band 0x100) -eq 0x100) {
            $FilesCount += 1
            $Entry = $_.FullName + $Delimiter + $_.GetAccessControl().Owner + "`r`n"
            $Entry
            $Entry | Out-File -FilePath $LogFilePath -Encoding unicode -Append -NoNewline
            $_.Attributes = ($_.Attributes -band 0xFEFF)
        }
    }
}

$FinishTime = Get-Date
$TimeSpan = $FinishTime - $StartTime
Write-Output ("Done, errors: " + $Error.Count)

$Encoding = [System.Text.Encoding]::Unicode

If ($FilesCount -gt 0) {
    $MessageBody = $null
    $Subject = "Обнаружены временные файлы в реплицируемых папках"
    $MessageBody += "Имя компьютера: $env:COMPUTERNAME`r`n"
    $MessageBody +=  "Общее время сканирования: " + $TimeSpan.ToString() + "`r`n"
    $MessageBody += "Подробности в файле `"$LogFilePath`"`r`n"
    Send-MailMessage -SmtpServer $SMTPServer -From $MailFrom -Subject $Subject -Encoding $Encoding -To $MailTo -Body $MessageBody -Attachments $LogFilePath
}
Else {
    $MessageBody = $null
    $Subject = "Временные файлы в реплицируемых папках не обнаружены"
    $MessageBody += "Имя компьютера: $env:COMPUTERNAME`r`n"
    $MessageBody +=  "Общее время сканирования: " + $TimeSpan.ToString() + "`r`n"
    Send-MailMessage -SmtpServer $SMTPServer -From $MailFrom -Subject $Subject -Encoding $Encoding -To $MailTo -Body $MessageBody
}

#Уведомляем, если что-то пошло не так
If ($Error.Count -gt 0) {
    $MessageBody = $null
    $Subject = "Обработка временных файлов завершена с ошибкой"
    $MessageBody += "Имя компьютера: $env:COMPUTERNAME`r`n"
    $MessageBody += "Учетная запись: $env:UserName`r`n"
    $MessageBody += "Последняя ошибка:`r`n"
    $MessageBody += $Error[0]
    Send-MailMessage -SmtpServer $SMTPServer -From $MailFrom -Subject $Subject -Encoding $Encoding -To $MailTo -Body $MessageBody
}


Правим значения переменных, отвечающих за email-оповещения, и добавляем скрипт в планировщик:

powershell.exe -NoLogo -ExecutionPolicy Bypass -NoProfile -File "<папка со скриптом>\TempAttrFixer.ps1"

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

Вместо отчетов на email (либо в дополнение к ним) можно настроить взаимодействие скрипта с заббиксом или другой системой мониторинга. Также важно понимать, что процедура достаточно ресурсоёмкая, поэтому не надо запускать ее слишком часто. У нас на обработку 10ТБ данных уходит примерно час, а скрипт запускается раз в сутки по ночам.

Еще нас с коллегами интересовал вопрос, откуда берутся файлы с temporary-атрибутом. Поэтому в каждом отчете вместе с полным именем файла фигурирует его NTFS-владелец. На основании собранных данных удалось выяснить, что в нашей ситуации атрибут иногда добавлялся к файлам во время копирования данных с локальных дисков, проброшенных по RDP (у нас активно используется технология удаленных рабочих столов). Но не исключено, что файлы изначально были «дефектными». Пока выяснить более подробно не удалось.

Буду благодарен, если вы пройдете краткий опрос и поделитесь своим опытом в комментариях.

UPD 23.07.2020: В Windows 2008 R2 нет штатного PS-модуля для работы с DFSR. В качестве workaround для нее можно установить этот модуль, а в скрипте TempAttrFixer.ps1 заменить одну строку, отвечающую за поиск реплицируемых папок:
$FoldersToScan = @(Get-DfsrFolderMembership -ComputerName $env:COMPUTERNAME | Sort-Object Name).LocalPath

(за уточнение спасибо mordaden)
Only registered users can participate in poll. Log in, please.
Только для тех, кто использует DFSR: знали ли вы до прочтения статьи о влиянии атрибута «временный» на репликацию файлов?
15.15% Знал, но руки не доходили5
0% Знал, уже принял меры0
51.52% Не знал, буду разбираться17
33.33% Не знал, но сейчас не до этого11
33 users voted. 15 users abstained.
Tags:
Hubs:
+22
Comments0

Articles

Change theme settings