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) } }) } }