Expand capture-only NetScaler NITRO simulation and crash recovery

This commit is contained in:
StasonJatham committed 2026-10-04 06:16:31 +02:00
1 parent 7b99db9e25
commit c65f049223
10 files changed
+2097 -19

No files matched your search

+24 -2
View File
@@ -6,6 +6,7 @@ import os
import pathlib
import threading
import time
import tempfile
class EvidenceError(OSError):
@@ -25,6 +26,21 @@ class Evidence:
self.used = sum(p.stat().st_size for p in self.directory.rglob('*') if p.is_file())
self.journal = os.open(self.directory / 'events.jsonl', os.O_WRONLY | os.O_APPEND | os.O_CREAT | os.O_NOFOLLOW, 0o600)
os.fchmod(self.journal, 0o600)
# Preserve a crash-interrupted record without merging the next event into it.
with (self.directory / 'events.jsonl').open('rb') as previous:
previous.seek(0, os.SEEK_END)
if previous.tell():
previous.seek(-1, os.SEEK_END)
if previous.read(1) != b'\n':
self.reserve(1)
os.write(self.journal, b'\n')
os.fsync(self.journal)
self.used += 1
directory = os.open(self.directory, os.O_RDONLY)
try:
os.fsync(directory)
finally:
os.close(directory)
def reserve(self, size):
if self.used + size > self.quota:
@@ -50,14 +66,20 @@ class Evidence:
with self.lock:
if not path.exists():
self.reserve(len(data))
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
fd, temporary = tempfile.mkstemp(prefix='.capture-', dir=self.artifacts)
try:
with os.fdopen(fd, 'wb') as output:
output.write(data)
output.flush()
os.fsync(output.fileno())
os.replace(temporary, path)
directory = os.open(self.artifacts, os.O_RDONLY)
try:
os.fsync(directory)
finally:
os.close(directory)
except BaseException:
path.unlink(missing_ok=True) # do not leave a partial blob with a full-content hash
pathlib.Path(temporary).unlink(missing_ok=True)
raise
self.used += len(data)
return dict(sha256=digest, bytes=len(data), artifact='artifacts/' + path.name)