New 'arca add <device>' subcommand detects a LUKS device via udisks2 and appends it to the config with passphrase as default method. Supports --alias/-a to override the generated name. Skips if UUID already configured. Adds Config.Save() and Config.HasUUID() to config package. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
127 lines
2.9 KiB
Go
127 lines
2.9 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Config holds the arca configuration.
|
|
type Config struct {
|
|
Devices map[string]DeviceConfig `yaml:"devices"`
|
|
}
|
|
|
|
// DeviceConfig holds per-device settings.
|
|
type DeviceConfig struct {
|
|
UUID string `yaml:"uuid"`
|
|
Mountpoint string `yaml:"mountpoint,omitempty"`
|
|
Methods []string `yaml:"methods,omitempty"`
|
|
Keyfile string `yaml:"keyfile,omitempty"`
|
|
}
|
|
|
|
// ResolvedDevice holds the effective settings for a device lookup.
|
|
type ResolvedDevice struct {
|
|
UUID string
|
|
Mountpoint string
|
|
Methods []string
|
|
Keyfile string
|
|
}
|
|
|
|
// Load reads the config file. Returns an empty config if the file doesn't exist.
|
|
func Load() *Config {
|
|
cfg := &Config{
|
|
Devices: make(map[string]DeviceConfig),
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath())
|
|
if err != nil {
|
|
return cfg
|
|
}
|
|
|
|
yaml.Unmarshal(data, cfg)
|
|
return cfg
|
|
}
|
|
|
|
// ResolveDevice looks up by alias name first, then checks if the argument
|
|
// is a device path matching a known alias (e.g. "/dev/sda1" matches "sda1").
|
|
// If nothing matches, returns defaults for a bare device path.
|
|
func (c *Config) ResolveDevice(nameOrPath string) ResolvedDevice {
|
|
// Direct alias match.
|
|
if dev, ok := c.Devices[nameOrPath]; ok {
|
|
return resolvedFrom(dev)
|
|
}
|
|
|
|
// Match device path against aliases: "/dev/sda1" matches alias "sda1".
|
|
base := filepath.Base(nameOrPath)
|
|
if dev, ok := c.Devices[base]; ok {
|
|
return resolvedFrom(dev)
|
|
}
|
|
|
|
return ResolvedDevice{
|
|
Methods: []string{"passphrase"},
|
|
}
|
|
}
|
|
|
|
func resolvedFrom(dev DeviceConfig) ResolvedDevice {
|
|
methods := dev.Methods
|
|
if len(methods) == 0 {
|
|
methods = []string{"passphrase"}
|
|
}
|
|
return ResolvedDevice{
|
|
UUID: dev.UUID,
|
|
Mountpoint: dev.Mountpoint,
|
|
Methods: methods,
|
|
Keyfile: dev.Keyfile,
|
|
}
|
|
}
|
|
|
|
// HasUUID returns true if a device with the given UUID is already configured.
|
|
func (c *Config) HasUUID(uuid string) bool {
|
|
for _, dev := range c.Devices {
|
|
if dev.UUID == uuid {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Save writes the config to the config file.
|
|
func (c *Config) Save() error {
|
|
path := configPath()
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
return fmt.Errorf("creating config directory: %w", err)
|
|
}
|
|
|
|
data, err := yaml.Marshal(c)
|
|
if err != nil {
|
|
return fmt.Errorf("marshaling config: %w", err)
|
|
}
|
|
|
|
return os.WriteFile(path, data, 0o644)
|
|
}
|
|
|
|
// AliasFor returns the config alias for a given UUID, or "" if none.
|
|
func (c *Config) AliasFor(uuid string) string {
|
|
for name, dev := range c.Devices {
|
|
if dev.UUID == uuid {
|
|
return name
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Path returns the config file path, respecting XDG_CONFIG_HOME.
|
|
func Path() string {
|
|
return configPath()
|
|
}
|
|
|
|
func configPath() string {
|
|
if dir := os.Getenv("XDG_CONFIG_HOME"); dir != "" {
|
|
return filepath.Join(dir, "arca", "config.yaml")
|
|
}
|
|
home, _ := os.UserHomeDir()
|
|
return filepath.Join(home, ".config", "arca", "config.yaml")
|
|
}
|