Replace symlinks with plain files containing relative paths
- Remove all symlink creation and resolution in favor of plain files - currentvault file now contains relative path like "vaults.d/default" - current-unlocker file now contains relative path like "unlockers.d/passphrase" - current version file now contains relative path like "versions/20231215.001" - Simplify path resolution to just read file contents and join with parent dir - Update all tests to read files instead of using os.Readlink
This commit is contained in:
@@ -98,38 +98,28 @@ func (v *Vault) GetCurrentUnlocker() (secret.Unlocker, error) {
|
||||
return unlocker, nil
|
||||
}
|
||||
|
||||
// resolveUnlockerDirectory resolves the unlocker directory from a symlink or file
|
||||
// resolveUnlockerDirectory reads the current-unlocker file to get the unlocker directory path
|
||||
// The file contains a relative path to the unlocker directory
|
||||
func (v *Vault) resolveUnlockerDirectory(currentUnlockerPath string) (string, error) {
|
||||
linkReader, ok := v.fs.(afero.LinkReader)
|
||||
if !ok {
|
||||
// Fallback for filesystems that don't support symlinks
|
||||
return v.readUnlockerPathFromFile(currentUnlockerPath)
|
||||
}
|
||||
secret.Debug("Reading current-unlocker file", "path", currentUnlockerPath)
|
||||
|
||||
secret.Debug("Resolving unlocker symlink using afero")
|
||||
// Try to read as symlink first
|
||||
unlockerDir, err := linkReader.ReadlinkIfPossible(currentUnlockerPath)
|
||||
if err == nil {
|
||||
return unlockerDir, nil
|
||||
}
|
||||
|
||||
secret.Debug("Failed to read symlink, falling back to file contents",
|
||||
"error", err, "symlink_path", currentUnlockerPath)
|
||||
// Fallback: read the path from file contents
|
||||
return v.readUnlockerPathFromFile(currentUnlockerPath)
|
||||
}
|
||||
|
||||
// readUnlockerPathFromFile reads the unlocker directory path from a file
|
||||
func (v *Vault) readUnlockerPathFromFile(path string) (string, error) {
|
||||
secret.Debug("Reading unlocker path from file", "path", path)
|
||||
unlockerDirBytes, err := afero.ReadFile(v.fs, path)
|
||||
unlockerDirBytes, err := afero.ReadFile(v.fs, currentUnlockerPath)
|
||||
if err != nil {
|
||||
secret.Debug("Failed to read unlocker path file", "error", err, "path", path)
|
||||
secret.Debug("Failed to read current-unlocker file", "error", err, "path", currentUnlockerPath)
|
||||
|
||||
return "", fmt.Errorf("failed to read current unlocker: %w", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(unlockerDirBytes)), nil
|
||||
relativePath := strings.TrimSpace(string(unlockerDirBytes))
|
||||
secret.Debug("Read relative path from file", "relative_path", relativePath)
|
||||
|
||||
// Resolve to absolute path relative to the vault directory
|
||||
vaultDir := filepath.Dir(currentUnlockerPath)
|
||||
absolutePath := filepath.Join(vaultDir, relativePath)
|
||||
|
||||
secret.Debug("Resolved to absolute path", "absolute_path", absolutePath)
|
||||
|
||||
return absolutePath, nil
|
||||
}
|
||||
|
||||
// findUnlockerByID finds an unlocker by its ID and returns the unlocker instance and its directory path
|
||||
@@ -287,30 +277,28 @@ func (v *Vault) SelectUnlocker(unlockerID string) error {
|
||||
return fmt.Errorf("unlocker with ID %s not found", unlockerID)
|
||||
}
|
||||
|
||||
// Create/update current unlocker symlink
|
||||
// Create/update current-unlocker file with relative path
|
||||
currentUnlockerPath := filepath.Join(vaultDir, "current-unlocker")
|
||||
|
||||
// Remove existing symlink if it exists
|
||||
// Remove existing file if it exists
|
||||
if exists, err := afero.Exists(v.fs, currentUnlockerPath); err != nil {
|
||||
return fmt.Errorf("failed to check if current unlocker symlink exists: %w", err)
|
||||
return fmt.Errorf("failed to check if current-unlocker file exists: %w", err)
|
||||
} else if exists {
|
||||
if err := v.fs.Remove(currentUnlockerPath); err != nil {
|
||||
return fmt.Errorf("failed to remove existing unlocker symlink: %w", err)
|
||||
return fmt.Errorf("failed to remove existing current-unlocker file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create new symlink using afero's SymlinkIfPossible
|
||||
if linker, ok := v.fs.(afero.Linker); ok {
|
||||
secret.Debug("Creating unlocker symlink", "target", targetUnlockerDir, "link", currentUnlockerPath)
|
||||
if err := linker.SymlinkIfPossible(targetUnlockerDir, currentUnlockerPath); err != nil {
|
||||
return fmt.Errorf("failed to create unlocker symlink: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Fallback: create a regular file with the target path for filesystems that don't support symlinks
|
||||
secret.Debug("Fallback: creating regular file with target path", "target", targetUnlockerDir)
|
||||
if err := afero.WriteFile(v.fs, currentUnlockerPath, []byte(targetUnlockerDir), secret.FilePerms); err != nil {
|
||||
return fmt.Errorf("failed to create unlocker symlink file: %w", err)
|
||||
}
|
||||
// Compute relative path from vault directory to unlocker directory
|
||||
relativePath, err := filepath.Rel(vaultDir, targetUnlockerDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compute relative path: %w", err)
|
||||
}
|
||||
|
||||
// Write the relative path to the file
|
||||
secret.Debug("Writing current-unlocker file", "relative_path", relativePath)
|
||||
if err := afero.WriteFile(v.fs, currentUnlockerPath, []byte(relativePath), secret.FilePerms); err != nil {
|
||||
return fmt.Errorf("failed to create current-unlocker file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user