Files
arca/cmd/unmount.go
Kyle Isom e44dd382dd M6: shell completions with dynamic alias lookup
Add ValidArgsFunction to mount and unmount commands that reads config
aliases for tab completion. Install zsh, bash, and fish completion
scripts via flake postInstall. Update PLAN.md with post-1.0 roadmap.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:35:19 -07:00

70 lines
1.7 KiB
Go

package cmd
import (
"fmt"
"git.wntrmute.dev/kyle/arca/internal/config"
"git.wntrmute.dev/kyle/arca/internal/cryptsetup"
"git.wntrmute.dev/kyle/arca/internal/udisks"
"github.com/spf13/cobra"
)
var unmountCmd = &cobra.Command{
Use: "unmount <device|alias>",
Aliases: []string{"umount"},
Short: "Unmount and lock a LUKS volume",
Args: cobra.ExactArgs(1),
RunE: runUnmount,
ValidArgsFunction: completeDeviceOrAlias,
}
func init() {
rootCmd.AddCommand(unmountCmd)
}
func runUnmount(cmd *cobra.Command, args []string) error {
cfg := config.Load()
target := args[0]
client, err := udisks.NewClient()
if err != nil {
return fmt.Errorf("connecting to udisks2: %w", err)
}
defer client.Close()
devCfg := cfg.ResolveDevice(target)
dev, err := client.FindDevice(devCfg.UUID, target)
if err != nil {
return err
}
// Check if already locked.
cleartext, err := client.CleartextDevice(dev)
if err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "%s is already locked\n", target)
return nil
}
// Unmount if mounted.
if mp, mounted := client.IsMounted(cleartext); mounted {
if err := client.Unmount(cleartext); err != nil {
// udisks2 unmount failed — try privileged umount.
if err := cryptsetup.Unmount(mp); err != nil {
return fmt.Errorf("unmounting: %w", err)
}
}
}
// Lock: try udisks2 first, fall back to cryptsetup close.
if err := client.Lock(dev); err != nil {
mapperName := cryptsetup.MapperName(dev.DevicePath)
if err := cryptsetup.Close(mapperName); err != nil {
return fmt.Errorf("locking: %w", err)
}
}
fmt.Printf("Unmounted and locked %s\n", target)
return nil
}