Lock the state directory and write vault files atomically (closes #34)
check / check (push) Successful in 50s
check / check (push) Successful in 50s
Each command that changes the state directory holds one lock: flock(2) on `lock` in the state directory, dropped by the kernel if the process dies, or a process-wide mutex on the in-memory test filesystem. It covers the state directory, not each vault, because `currentvault`, `vault create` and cross-vault moves span vaults, and a lock file in a vault would be deleted by `vault remove` under a waiting command. Files go through `secret.WriteFileAtomic`; versions, new secrets and cross-vault copies are built in a temporary directory and renamed into place; removals rename out of the way first. An unlocker added under an existing unlocker's directory name is still rewritten file by file: #71. Model: opus-5-5
This commit is contained in:
@@ -169,7 +169,7 @@ func processMnemonicForVault(
|
||||
|
||||
ltPubKeyPath := filepath.Join(vaultDir, "pub.age")
|
||||
|
||||
err = afero.WriteFile(fs, ltPubKeyPath, []byte(ltPubKey), secret.FilePerms)
|
||||
err = secret.WriteFileAtomic(fs, ltPubKeyPath, []byte(ltPubKey))
|
||||
if err != nil {
|
||||
return 0, "", "", fmt.Errorf("failed to write long-term public key: %w", err)
|
||||
}
|
||||
@@ -295,21 +295,13 @@ func SelectVault(fs afero.Fs, stateDir string, name string) error {
|
||||
return fmt.Errorf("vault %s %w", name, ErrVaultNotFound)
|
||||
}
|
||||
|
||||
// Create or update the currentvault file with just the vault name
|
||||
// Create or replace the currentvault file with just the vault name. It
|
||||
// is replaced in one rename, so it never goes missing.
|
||||
currentVaultPath := filepath.Join(stateDir, "currentvault")
|
||||
|
||||
// Remove existing file if it exists
|
||||
_, err = fs.Stat(currentVaultPath)
|
||||
if err == nil {
|
||||
secret.Debug("Removing existing currentvault file", "path", currentVaultPath)
|
||||
|
||||
_ = fs.Remove(currentVaultPath)
|
||||
}
|
||||
|
||||
// Write just the vault name to the file
|
||||
secret.Debug("Writing currentvault file", "vault_name", name)
|
||||
|
||||
err = afero.WriteFile(fs, currentVaultPath, []byte(name), secret.FilePerms)
|
||||
err = secret.WriteFileAtomic(fs, currentVaultPath, []byte(name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to select vault: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user