feat(go): durable transaction engine and Kiro IDE 1.0 adapter
Split the monolithic transaction into focused modules: append-only crash-recoverable journal under ~/.elydora/transactions with owner tokens and per-transaction namespaces, staged atomic publication (stage .pending, fsync, rename, fsync), platform-specific atomic replacement (MoveFileEx, renameat2, renamex_np), private-directory hardening per OS, lock-guarded recovery, and workspace cleanup that removes only transaction-created empty directories. Add the Kiro IDE 1.0 plugin: strict v1 hook schema with one enabled PreToolUse guard and PostToolUse audit pair bound to the active workspace, agent-to-workspace identity binding in runtime config, and exact migration of the legacy elydora-audit.kiro.hook contract. Gates: go test ./... and -race pass, go vet clean, govulncheck reports zero reachable vulnerabilities, git diff --check clean.
This commit is contained in:
1 parent
dd72793cdb
commit
eb41b991bf
70 files changed
+8182
-358
No files matched your search
@@ -0,0 +1,145 @@
|
||||
//go:build windows
|
||||
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func atomicRenameNoReplace(source, destination string) error {
|
||||
from, err := windows.UTF16PtrFromString(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to, err := windows.UTF16PtrFromString(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := windows.MoveFileEx(from, to, windows.MOVEFILE_WRITE_THROUGH); err != nil {
|
||||
return err
|
||||
}
|
||||
return syncWindowsRenameDirectories(source, destination)
|
||||
}
|
||||
|
||||
func isAtomicDestinationExists(err error) bool {
|
||||
return errors.Is(err, windows.ERROR_ALREADY_EXISTS) ||
|
||||
errors.Is(err, windows.ERROR_FILE_EXISTS)
|
||||
}
|
||||
|
||||
func atomicReplaceWithBackup(target, replacement, backup string) error {
|
||||
backupPointer, err := windows.UTF16PtrFromString(backup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetPointer, err := windows.UTF16PtrFromString(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureWindowsAtomicBackup(
|
||||
target,
|
||||
backup,
|
||||
targetPointer,
|
||||
backupPointer,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
replacementPointer, err := windows.UTF16PtrFromString(replacement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for {
|
||||
err = windows.MoveFileEx(
|
||||
replacementPointer,
|
||||
targetPointer,
|
||||
windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH,
|
||||
)
|
||||
if err == nil {
|
||||
return syncWindowsRenameDirectories(replacement, target)
|
||||
}
|
||||
if time.Now().After(deadline) ||
|
||||
!errors.Is(err, windows.ERROR_ACCESS_DENIED) &&
|
||||
!errors.Is(err, windows.ERROR_SHARING_VIOLATION) {
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureWindowsAtomicBackup(
|
||||
target, backup string,
|
||||
targetPointer, backupPointer *uint16,
|
||||
) error {
|
||||
err := windows.CreateHardLink(backupPointer, targetPointer, 0)
|
||||
if err != nil && !errors.Is(err, windows.ERROR_ALREADY_EXISTS) &&
|
||||
!errors.Is(err, windows.ERROR_FILE_EXISTS) {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
targetInfo, targetErr := os.Lstat(target)
|
||||
backupInfo, backupErr := os.Lstat(backup)
|
||||
if targetErr != nil || backupErr != nil ||
|
||||
targetInfo.Mode()&os.ModeSymlink != 0 ||
|
||||
backupInfo.Mode()&os.ModeSymlink != 0 ||
|
||||
!targetInfo.Mode().IsRegular() || !backupInfo.Mode().IsRegular() ||
|
||||
!os.SameFile(targetInfo, backupInfo) {
|
||||
return errors.Join(
|
||||
fmt.Errorf("atomic backup path is occupied by another object: %s", backup),
|
||||
targetErr,
|
||||
backupErr,
|
||||
)
|
||||
}
|
||||
}
|
||||
if err := syncTransactionDirectory(filepath.Dir(backup)); err != nil {
|
||||
return fmt.Errorf("flush atomic backup directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func atomicReplacementUsesSeparateBackup() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func transactionPlatformSupported() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncWindowsRenameDirectories(source, destination string) error {
|
||||
destinationDirectory := filepath.Dir(destination)
|
||||
destinationErr := syncTransactionDirectory(destinationDirectory)
|
||||
if durablePathKey(filepath.Dir(source)) == durablePathKey(destinationDirectory) {
|
||||
return destinationErr
|
||||
}
|
||||
return errors.Join(destinationErr, syncTransactionDirectory(filepath.Dir(source)))
|
||||
}
|
||||
|
||||
func syncTransactionDirectory(path string) error {
|
||||
pointer, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handle, err := windows.CreateFile(
|
||||
pointer,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
|
||||
nil,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_WRITE_THROUGH,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open directory for durable flush at %s: %w", path, err)
|
||||
}
|
||||
flushErr := windows.FlushFileBuffers(handle)
|
||||
closeErr := windows.CloseHandle(handle)
|
||||
if flushErr != nil {
|
||||
flushErr = fmt.Errorf("flush directory metadata at %s: %w", path, flushErr)
|
||||
}
|
||||
return errors.Join(flushErr, closeErr)
|
||||
}
|
||||
Reference in new issue
Block a user