summaryrefslogtreecommitdiff
path: root/src/Agent.Windows/Recovery/RecoveryManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Agent.Windows/Recovery/RecoveryManager.cs')
-rw-r--r--src/Agent.Windows/Recovery/RecoveryManager.cs13
1 files changed, 13 insertions, 0 deletions
diff --git a/src/Agent.Windows/Recovery/RecoveryManager.cs b/src/Agent.Windows/Recovery/RecoveryManager.cs
index 8fd7368..c9b28bf 100644
--- a/src/Agent.Windows/Recovery/RecoveryManager.cs
+++ b/src/Agent.Windows/Recovery/RecoveryManager.cs
@@ -49,6 +49,7 @@ public sealed class RecoveryManager(string journalDirectory, ISecurityAuditSink
{
case "file.replace": RecoverFile(entry); break;
case "file.delete": RecoverDeletedFile(entry); break;
+ case "file.rename": RecoverRenamedFile(entry); break;
case "dir.mkdir": RecoverMkdir(entry); break;
case "dir.rmdir": RecoverRmdir(entry); break;
case "acl": RecoverAcl(entry); break;
@@ -96,6 +97,18 @@ public sealed class RecoveryManager(string journalDirectory, ISecurityAuditSink
File.WriteAllBytes(entry.Target, original);
}
+ private static void RecoverRenamedFile(JournalEntry entry)
+ {
+ string destination = entry.Data["destination_path"];
+ string sourceHash = entry.Data["source_sha256"];
+ if (!File.Exists(destination)) return; // Already rolled back or destination missing — skip.
+ if (!Hash(destination).Equals(sourceHash, StringComparison.Ordinal))
+ throw new InvalidOperationException($"Renamed destination '{destination}' hash does not match journal record; automatic recovery refused.");
+ if (File.Exists(entry.Target))
+ throw new InvalidOperationException($"Source path '{entry.Target}' was recreated after crash; automatic recovery refused.");
+ File.Move(destination, entry.Target);
+ }
+
private static void RecoverMkdir(JournalEntry entry)
{
if (!Directory.Exists(entry.Target)) return;