diff --git a/site/content/docs/Advanced/Actions/_index.md b/site/content/docs/Advanced/Actions/_index.md index 9f3bb87f8..64dfe1864 100644 --- a/site/content/docs/Advanced/Actions/_index.md +++ b/site/content/docs/Advanced/Actions/_index.md @@ -143,41 +143,71 @@ zfs destroy $ZPOOL_NAME@$KOPIA_SNAPSHOT_ID When backing up files opened with exclusive lock in Windows, Kopia would fail the snapshot task because it can't read the file content. One of the popular solutions is taking a [shadow copy](https://en.wikipedia.org/wiki/Shadow_Copy) of the storage volume and ask Kopia to backup that instead. -In this example, we will use PowerShell to take a shadow copy in the "before" action of the target directory and clean everything up in the "after" action. +In this example, we will use [PowerShell](https://github.com/PowerShell/PowerShell/) to take a shadow copy in the "before" action of the target directory and clean everything up in the "after" action. +The script also self-elevates as administrator (required to take shadow copy) if Kopia is ran with an unprivileged account. + +Make sure `pwsh` is reachable in the PATH environment variable. before.ps1: ```powershell -$sourceDrive = Split-Path -Qualifier $env:KOPIA_SOURCE_PATH -$sourcePath = Split-Path -NoQualifier $env:KOPIA_SOURCE_PATH -# use Kopia snapshot ID as mount point name for extra caution for duplication -$mountPoint = "${PSScriptRoot}\${env:KOPIA_SNAPSHOT_ID}" - -$shadowId = (Invoke-CimMethod -ClassName Win32_ShadowCopy -MethodName Create -Arguments @{ Volume = "${sourceDrive}\" }).ShadowID -$shadowDevice = (Get-CimInstance -ClassName Win32_ShadowCopy | Where-Object { $_.ID -eq $shadowId }).DeviceObject -if (-not $shadowDevice) { - # fail the Kopia snapshot early if shadow copy was not created - exit 1 +if ($args.Length -eq 0) { + $kopiaSnapshotId = $env:KOPIA_SNAPSHOT_ID + $kopiaSourcePath = $env:KOPIA_SOURCE_PATH +} else { + $kopiaSnapshotId = $args[0] + $kopiaSourcePath = $args[1] +} + +$sourceDrive = Split-Path -Qualifier $kopiaSourcePath +$sourcePath = Split-Path -NoQualifier $kopiaSourcePath +# use Kopia snapshot ID as mount point name for extra caution for duplication +$mountPoint = "${PSScriptRoot}\${kopiaSnapshotId}" + +if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + $shadowId = (Invoke-CimMethod -ClassName Win32_ShadowCopy -MethodName Create -Arguments @{ Volume = "${sourceDrive}\" }).ShadowID + $shadowDevice = (Get-CimInstance -ClassName Win32_ShadowCopy | Where-Object { $_.ID -eq $shadowId }).DeviceObject + if (-not $shadowDevice) { + # fail the Kopia snapshot early if shadow copy was not created + exit 1 + } + + cmd /c mklink /d $mountPoint "${shadowDevice}\" +} else { + $proc = Start-Process 'pwsh' '-f', $MyInvocation.MyCommand.Path, $kopiaSnapshotId, $kopiaSourcePath -PassThru -Verb RunAs -WindowStyle Hidden -Wait + if ($proc.ExitCode) { + exit $proc.ExitCode + } } -cmd /c mklink /d $mountPoint "${shadowDevice}\" Write-Output "KOPIA_SNAPSHOT_PATH=${mountPoint}${sourcePath}" ``` after.ps1: ```powershell -$mountPoint = Get-Item "${PSScriptRoot}\${env:KOPIA_SNAPSHOT_ID}" -$mountedVolume = $mountPoint.Target -Remove-Item $mountPoint -Get-CimInstance -ClassName Win32_ShadowCopy | Where-Object { "$($_.DeviceObject)\" -eq "\\?\${mountedVolume}" } | Remove-CimInstance +if ($args.Length -eq 0) { + $kopiaSnapshotId = $env:KOPIA_SNAPSHOT_ID +} else { + $kopiaSnapshotId = $args[0] +} + +if (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + $mountPoint = Get-Item "${PSScriptRoot}\${kopiaSnapshotId}" + $mountedVolume = $mountPoint.Target + + Remove-Item $mountPoint + Get-CimInstance -ClassName Win32_ShadowCopy | Where-Object { "$($_.DeviceObject)\" -eq "\\?\${mountedVolume}" } | Remove-CimInstance +} else { + Start-Process 'pwsh' '-f', $MyInvocation.MyCommand.Path, $kopiaSnapshotId -Verb RunAs -WindowStyle Hidden -Wait +} ``` To install the actions: -``` -kopia policy set --before-folder-action 'pwsh -WindowStyle Hidden \before.ps1' -kopia policy set --after-folder-action 'pwsh -WindowStyle Hidden \after.ps1' +```shell +kopia policy set --before-folder-action "pwsh -WindowStyle Hidden \before.ps1" +kopia policy set --after-folder-action "pwsh -WindowStyle Hidden \after.ps1" ``` #### Contributions Welcome