Files
arca/cmd/mount.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

117 lines
2.7 KiB
Go

package cmd
import (
"fmt"
"os"
"git.wntrmute.dev/kyle/arca/internal/config"
"git.wntrmute.dev/kyle/arca/internal/cryptsetup"
"git.wntrmute.dev/kyle/arca/internal/udisks"
"git.wntrmute.dev/kyle/arca/internal/unlock"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var mountpoint string
var mountCmd = &cobra.Command{
Use: "mount <device|alias>",
Short: "Unlock and mount a LUKS volume",
Args: cobra.ExactArgs(1),
RunE: runMount,
ValidArgsFunction: completeDeviceOrAlias,
}
func init() {
mountCmd.Flags().StringVarP(&mountpoint, "mountpoint", "m", "", "mount point override (privileged path only)")
rootCmd.AddCommand(mountCmd)
}
func runMount(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)
// CLI flag overrides config mountpoint.
mp := devCfg.Mountpoint
if mountpoint != "" {
mp = mountpoint
}
dev, err := client.FindDevice(devCfg.UUID, target)
if err != nil {
return err
}
// Check if already unlocked.
if cleartext, err := client.CleartextDevice(dev); err == nil {
// Already unlocked — check if mounted too.
if existing, mounted := client.IsMounted(cleartext); mounted {
fmt.Println(existing)
return nil
}
// Unlocked but not mounted — just mount it.
return doMount(client, cleartext, mp)
}
// Need to unlock.
u := unlock.New(client, unlock.Options{
ReadPassphrase: readPassphrase,
KeyfilePath: devCfg.Keyfile,
})
result, err := u.Unlock(dev, devCfg.Methods)
if err != nil {
return err
}
if result.Privileged {
mnt, err := cryptsetup.Mount(result.Device.DevicePath, mp)
if err != nil {
return fmt.Errorf("mounting: %w", err)
}
fmt.Println(mnt)
return nil
}
if mp != "" {
fmt.Fprintf(os.Stderr, "warning: --mountpoint is ignored for udisks2 mounts (passphrase/keyfile path)\n")
}
return doMount(client, result.Device, "")
}
func doMount(client *udisks.Client, cleartext *udisks.BlockDevice, mp string) error {
if mp != "" {
// udisks2 doesn't support custom mount points; use privileged mount.
mnt, err := cryptsetup.Mount(cleartext.DevicePath, mp)
if err != nil {
return fmt.Errorf("mounting: %w", err)
}
fmt.Println(mnt)
return nil
}
mnt, err := client.Mount(cleartext)
if err != nil {
return fmt.Errorf("mounting: %w", err)
}
fmt.Println(mnt)
return nil
}
func readPassphrase() (string, error) {
fmt.Fprint(os.Stderr, "Passphrase: ")
pass, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Fprintln(os.Stderr)
if err != nil {
return "", fmt.Errorf("reading passphrase: %w", err)
}
return string(pass), nil
}