Add NTFS stable folder ID demo script
This commit is contained in:
172
Sonstiges/Get-NtfsStableFolderId.ps1
Normal file
172
Sonstiges/Get-NtfsStableFolderId.ps1
Normal file
@@ -0,0 +1,172 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string[]]$Path,
|
||||
|
||||
[Parameter()]
|
||||
[string]$ExportJsonPath
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) {
|
||||
throw 'Dieses Demo-Script kann nur unter Windows ausgefuehrt werden.'
|
||||
}
|
||||
|
||||
$nativeMethods = @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
public static class NtfsNativeMethods
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct BY_HANDLE_FILE_INFORMATION
|
||||
{
|
||||
public uint FileAttributes;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
|
||||
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
|
||||
public uint VolumeSerialNumber;
|
||||
public uint FileSizeHigh;
|
||||
public uint FileSizeLow;
|
||||
public uint NumberOfLinks;
|
||||
public uint FileIndexHigh;
|
||||
public uint FileIndexLow;
|
||||
}
|
||||
|
||||
public const uint FILE_SHARE_READ = 0x00000001;
|
||||
public const uint FILE_SHARE_WRITE = 0x00000002;
|
||||
public const uint FILE_SHARE_DELETE = 0x00000004;
|
||||
public const uint OPEN_EXISTING = 3;
|
||||
public const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern SafeFileHandle CreateFile(
|
||||
string lpFileName,
|
||||
uint dwDesiredAccess,
|
||||
uint dwShareMode,
|
||||
IntPtr lpSecurityAttributes,
|
||||
uint dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
IntPtr hTemplateFile);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetFileInformationByHandle(
|
||||
SafeFileHandle hFile,
|
||||
out BY_HANDLE_FILE_INFORMATION lpFileInformation);
|
||||
}
|
||||
"@
|
||||
|
||||
if (-not ('NtfsNativeMethods' -as [type])) {
|
||||
Add-Type -TypeDefinition $nativeMethods
|
||||
}
|
||||
|
||||
function Get-Md5Hex {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Text
|
||||
)
|
||||
|
||||
$md5 = [System.Security.Cryptography.MD5]::Create()
|
||||
try {
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
|
||||
$hash = $md5.ComputeHash($bytes)
|
||||
return ([System.BitConverter]::ToString($hash)).Replace('-', '').ToLowerInvariant()
|
||||
}
|
||||
finally {
|
||||
$md5.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
function Get-NtfsStableFolderId {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$LiteralPath
|
||||
)
|
||||
|
||||
$item = Get-Item -LiteralPath $LiteralPath -Force
|
||||
if (-not $item.PSIsContainer) {
|
||||
throw "Der Pfad '$LiteralPath' ist kein Ordner."
|
||||
}
|
||||
|
||||
$handle = [NtfsNativeMethods]::CreateFile(
|
||||
$item.FullName,
|
||||
0,
|
||||
[NtfsNativeMethods]::FILE_SHARE_READ -bor [NtfsNativeMethods]::FILE_SHARE_WRITE -bor [NtfsNativeMethods]::FILE_SHARE_DELETE,
|
||||
[IntPtr]::Zero,
|
||||
[NtfsNativeMethods]::OPEN_EXISTING,
|
||||
[NtfsNativeMethods]::FILE_FLAG_BACKUP_SEMANTICS,
|
||||
[IntPtr]::Zero)
|
||||
|
||||
if ($handle.IsInvalid) {
|
||||
$lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
throw "CreateFile fehlgeschlagen fuer '$($item.FullName)' mit Win32-Fehler $lastError."
|
||||
}
|
||||
|
||||
try {
|
||||
$info = New-Object NtfsNativeMethods+BY_HANDLE_FILE_INFORMATION
|
||||
if (-not [NtfsNativeMethods]::GetFileInformationByHandle($handle, [ref]$info)) {
|
||||
$lastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
|
||||
throw "GetFileInformationByHandle fehlgeschlagen fuer '$($item.FullName)' mit Win32-Fehler $lastError."
|
||||
}
|
||||
|
||||
$fileIndex = ([UInt64]$info.FileIndexHigh -shl 32) -bor [UInt64]$info.FileIndexLow
|
||||
$stableAnchor = ('{0:x8}:{1:x16}' -f $info.VolumeSerialNumber, $fileIndex)
|
||||
|
||||
[pscustomobject]@{
|
||||
Path = $item.FullName
|
||||
Name = $item.Name
|
||||
VolumeSerialNumberHex = ('0x{0:x8}' -f $info.VolumeSerialNumber)
|
||||
FileIndexHex = ('0x{0:x16}' -f $fileIndex)
|
||||
StableAnchor = $stableAnchor
|
||||
StableDataAreaId = (Get-Md5Hex -Text $stableAnchor)
|
||||
CreatedUtc = $item.CreationTimeUtc.ToString('o')
|
||||
LastWriteUtc = $item.LastWriteTimeUtc.ToString('o')
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$handle.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
$results = foreach ($currentPath in $Path) {
|
||||
Get-NtfsStableFolderId -LiteralPath $currentPath
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('ExportJsonPath')) {
|
||||
$exportDirectory = Split-Path -Parent $ExportJsonPath
|
||||
if (-not [string]::IsNullOrWhiteSpace($exportDirectory) -and -not (Test-Path -LiteralPath $exportDirectory)) {
|
||||
New-Item -ItemType Directory -Path $exportDirectory | Out-Null
|
||||
}
|
||||
|
||||
$results | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $ExportJsonPath -Encoding UTF8
|
||||
}
|
||||
|
||||
$results
|
||||
|
||||
<#
|
||||
Demo fuer Rename/Move auf demselben NTFS-Volume:
|
||||
|
||||
1. Vorher ausfuehren:
|
||||
.\Get-NtfsStableFolderId.ps1 -Path 'C:\Daten\TeamA' -ExportJsonPath '.\before.json'
|
||||
|
||||
2. Ordner umbenennen oder innerhalb desselben Volumes verschieben.
|
||||
|
||||
3. Nachher erneut ausfuehren:
|
||||
.\Get-NtfsStableFolderId.ps1 -Path 'C:\Daten\Fachbereich\TeamA_Renamed' -ExportJsonPath '.\after.json'
|
||||
|
||||
4. Vergleichen:
|
||||
- `Path` ist anders
|
||||
- `StableAnchor` bleibt gleich
|
||||
- `StableDataAreaId` bleibt gleich
|
||||
|
||||
Wichtige Einschraenkung:
|
||||
|
||||
- Rename und Move auf demselben NTFS-Volume behalten die Identitaet.
|
||||
- Copy-and-delete oder Move ueber ein anderes Volume erzeugen in der Regel eine neue Identitaet.
|
||||
- Auf manchen Remote-/DFS-Szenarien kann der Dateiserver diese Metadaten nicht verlaesslich liefern.
|
||||
#>
|
||||
Reference in New Issue
Block a user