Files
2026-06-24 17:29:45 +02:00

106 lines
3.6 KiB
Go

package storage
import (
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
func TestParseFstab(t *testing.T) {
data := `# /etc/fstab
UUID=1111-2222 / ext4 defaults 0 1
/dev/sdb1 /mnt/data ext4 rw,noatime 0 2
LABEL=swap none swap sw
`
got := parseFstab(data)
want := []FstabEntry{
{Device: "UUID=1111-2222", Mountpoint: "/", FSType: "ext4", Options: "defaults", Dump: 0, Pass: 1},
{Device: "/dev/sdb1", Mountpoint: "/mnt/data", FSType: "ext4", Options: "rw,noatime", Dump: 0, Pass: 2},
{Device: "LABEL=swap", Mountpoint: "none", FSType: "swap", Options: "sw", Dump: 0, Pass: 0},
}
if !reflect.DeepEqual(got, want) {
t.Errorf("parseFstab:\n got %+v\nwant %+v", got, want)
}
}
func TestFstabSurgicalEdits(t *testing.T) {
path := filepath.Join(t.TempDir(), "fstab")
old := fstabFile
fstabFile = path
defer func() { fstabFile = old }()
// Seed with a comment + an existing entry that must survive edits.
seed := "# keep me\nUUID=root / ext4 defaults 0 1\n"
if err := os.WriteFile(path, []byte(seed), 0644); err != nil {
t.Fatal(err)
}
// Append a new entry.
if err := appendFstabLine(FstabEntry{Device: "/dev/sdb1", Mountpoint: "/mnt/data", FSType: "xfs", Options: "noatime", Pass: 2}); err != nil {
t.Fatal(err)
}
entries, err := readFstab()
if err != nil {
t.Fatal(err)
}
if len(entries) != 2 || findEntry(entries, "/mnt/data") == nil {
t.Fatalf("append failed: %+v", entries)
}
// Remove it again; the comment and original entry must remain.
removed, err := removeFstabLines("/mnt/data")
if err != nil || !removed {
t.Fatalf("remove failed: removed=%v err=%v", removed, err)
}
data, _ := os.ReadFile(path)
if !strings.Contains(string(data), "# keep me") || !strings.Contains(string(data), "UUID=root") {
t.Errorf("surgical edit clobbered other lines:\n%s", data)
}
if findEntry(mustReadFstab(t), "/mnt/data") != nil {
t.Error("entry still present after remove")
}
// Removing a missing mountpoint reports not-removed.
if removed, _ := removeFstabLines("/nope"); removed {
t.Error("expected removed=false for missing mountpoint")
}
}
func mustReadFstab(t *testing.T) []FstabEntry {
t.Helper()
e, err := readFstab()
if err != nil {
t.Fatal(err)
}
return e
}
func TestValidateEntry(t *testing.T) {
for _, tt := range []struct {
name string
entry FstabEntry
valid bool
}{
{name: "valid", entry: FstabEntry{Device: "/dev/sdb1", Mountpoint: "/mnt/data", FSType: "ext4", Options: "defaults"}, valid: true},
{name: "UUID", entry: FstabEntry{Device: "UUID=ab-cd", Mountpoint: "/mnt/x", FSType: "xfs", Options: "rw,noatime"}, valid: true},
{name: "shell metachar in device", entry: FstabEntry{Device: "/dev/sdb1; rm -rf /", Mountpoint: "/mnt/x", FSType: "ext4", Options: "defaults"}, valid: false},
{name: "relative mountpoint", entry: FstabEntry{Device: "/dev/sdb1", Mountpoint: "../etc", FSType: "ext4", Options: "defaults"}, valid: false},
{name: "traversal in mountpoint", entry: FstabEntry{Device: "/dev/sdb1", Mountpoint: "/mnt/../../etc", FSType: "ext4", Options: "defaults"}, valid: false},
{name: "bad fstype", entry: FstabEntry{Device: "/dev/sdb1", Mountpoint: "/mnt/x", FSType: "ext4!", Options: "defaults"}, valid: false},
{name: "bad options", entry: FstabEntry{Device: "/dev/sdb1", Mountpoint: "/mnt/x", FSType: "ext4", Options: "defaults; reboot"}, valid: false},
} {
t.Run(tt.name, func(t *testing.T) {
err := validateEntry(tt.entry)
if tt.valid && err != nil {
t.Errorf("valid entry rejected: %v", err)
}
if !tt.valid && err == nil {
t.Errorf("bad entry accepted: %+v", tt.entry)
}
})
}
}