summaryrefslogtreecommitdiff
path: root/src/Agent.Windows/Recovery/TransactionalFileEditor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Agent.Windows/Recovery/TransactionalFileEditor.cs')
-rw-r--r--src/Agent.Windows/Recovery/TransactionalFileEditor.cs51
1 files changed, 51 insertions, 0 deletions
diff --git a/src/Agent.Windows/Recovery/TransactionalFileEditor.cs b/src/Agent.Windows/Recovery/TransactionalFileEditor.cs
index 52963a0..805a069 100644
--- a/src/Agent.Windows/Recovery/TransactionalFileEditor.cs
+++ b/src/Agent.Windows/Recovery/TransactionalFileEditor.cs
@@ -1,5 +1,6 @@
using System.Security.Cryptography;
using System.Text;
+using Agent.Core.Tools;
using Agent.Windows.Security;
namespace Agent.Windows.Recovery;
@@ -68,6 +69,30 @@ public sealed class TransactionalFileEditor(TaskResourceJournal journal, string
return new(journal, entryId, canonical, originalHash, backupPath, inline);
}
+ public DirectoryMkdirTransaction Mkdir(string path)
+ {
+ string canonical = CanonicalPath.Resolve(path, requireExisting: false);
+ bool existed = Directory.Exists(canonical);
+ if (existed) throw new InvalidOperationException($"Directory already exists: '{canonical}'.");
+ if (File.Exists(canonical)) throw new InvalidOperationException($"A file exists at the target path: '{canonical}'.");
+ Guid entryId = journal.Prepare("dir.mkdir", canonical, new Dictionary<string, string> { ["existed"] = existed.ToString() });
+ Directory.CreateDirectory(canonical);
+ journal.MarkApplied(entryId);
+ return new(journal, entryId, canonical);
+ }
+
+ public DirectoryRmdirTransaction Rmdir(string path)
+ {
+ string canonical = CanonicalPath.Resolve(path);
+ if (!Directory.Exists(canonical)) throw new DirectoryNotFoundException($"Directory does not exist: '{canonical}'.");
+ if (Directory.EnumerateFileSystemEntries(canonical, "*", SearchOption.TopDirectoryOnly).Any())
+ throw new ToolValidationException("Directory is not empty. Remove contents first before removing the directory.");
+ Guid entryId = journal.Prepare("dir.rmdir", canonical, new Dictionary<string, string>());
+ Directory.Delete(canonical, recursive: false);
+ journal.MarkApplied(entryId);
+ return new(journal, entryId, canonical);
+ }
+
private static string NormalizeHash(string hash) => hash.Replace("-", string.Empty, StringComparison.Ordinal).Trim().ToUpperInvariant();
}
@@ -93,6 +118,32 @@ public sealed class FileDeleteTransaction(TaskResourceJournal journal, Guid entr
}
}
+public sealed class DirectoryMkdirTransaction(TaskResourceJournal journal, Guid entryId, string path)
+{
+ private bool _finished;
+ public void Commit() { if (_finished) return; journal.MarkCleaned(entryId); _finished = true; }
+ public void Rollback()
+ {
+ if (_finished) return;
+ if (Directory.Exists(path)) Directory.Delete(path, recursive: false);
+ journal.MarkCleaned(entryId);
+ _finished = true;
+ }
+}
+
+public sealed class DirectoryRmdirTransaction(TaskResourceJournal journal, Guid entryId, string path)
+{
+ private bool _finished;
+ public void Commit() { if (_finished) return; journal.MarkCleaned(entryId); _finished = true; }
+ public void Rollback()
+ {
+ if (_finished) return;
+ if (!Directory.Exists(path)) Directory.CreateDirectory(path);
+ journal.MarkCleaned(entryId);
+ _finished = true;
+ }
+}
+
public sealed class FileEditTransaction(
TaskResourceJournal journal, Guid entryId, string path, string originalHash, string replacementHash,
string backupPath, string originalBase64, bool originallyExisted)