blob: dde39784a594a814a2dd245b07c8bca790840c89 [file] [log] [blame]
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +00001package fat32
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
7 "strings"
8 "testing"
9 "time"
10
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010011 "github.com/bazelbuild/rules_go/go/runfiles"
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000012)
13
Tim Windelschmidt82e6af72024-07-23 00:05:42 +000014var (
15 // These are filled by bazel at linking time with the canonical path of
16 // their corresponding file. Inside the init function we resolve it
17 // with the rules_go runfiles package to the real path.
18 xFsckPath string
19)
20
21func init() {
22 if os.Getenv("IN_KTEST") == "true" {
23 return
24 }
25
26 var err error
27 for _, path := range []*string{
28 &xFsckPath,
29 } {
30 *path, err = runfiles.Rlocation(*path)
31 if err != nil {
32 panic(err)
33 }
34 }
35}
36
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000037func testWithFsck(t *testing.T, rootInode Inode, opts Options) {
38 t.Helper()
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000039 testFile, err := os.CreateTemp("", "fat32-fsck-test")
40 if err != nil {
41 t.Fatal(err)
42 }
43 defer os.Remove(testFile.Name())
44 if err := WriteFS(testFile, rootInode, opts); err != nil {
45 t.Fatalf("failed to write test FS: %v", err)
46 }
47 // Run fsck non-interactively (-n), disallow spaces in short file names (-S)
48 // as well as perform deep verification (-V)
49 // If the file system is OK (i.e. fsck does not want to fix it) it returns
50 // 0, otherwise 1.
Tim Windelschmidt82e6af72024-07-23 00:05:42 +000051 fsckCmd := exec.Command(xFsckPath, "-n", "-S", "-V", testFile.Name())
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000052 result, err := fsckCmd.CombinedOutput()
53 if err != nil {
54 t.Errorf("fsck failed: %v", string(result))
55 }
56}
57
58func TestBasicFsck(t *testing.T) {
59 if os.Getenv("IN_KTEST") == "true" {
60 t.Skip("In ktest")
61 }
62 var largeString strings.Builder
63 for i := 0; i < 16384; i++ {
64 fmt.Fprintf(&largeString, "part%d", i)
65 }
66 // Test both common block sizes (512 and 4096 bytes) as well as the largest
67 // supported one (32K)
68 for _, blockSize := range []uint16{512, 4096, 32768} {
69 for _, fixed := range []string{"", "Fixed"} {
70 t.Run(fmt.Sprintf("BlockSize%d%v", blockSize, fixed), func(t *testing.T) {
71 rootInode := Inode{
72 Attrs: AttrDirectory,
73 ModTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC),
74 CreateTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC),
75 }
76 files := []struct {
77 name string
78 path string
79 content string
80 }{
81 {"FileInRoot", "test1.txt", "test1 content"},
82 {"LongFileInRoot", "verylongtest1.txt", "test1 content long"},
83 {"LongPath", "test1/test2/test3/test4/longdirname.ext/hello", "long path test content"},
84 {"LargeFile", "test1/largefile.txt", largeString.String()},
85 }
86 for _, c := range files {
87 err := rootInode.PlaceFile(c.path, strings.NewReader(c.content))
88 if err != nil {
89 t.Errorf("failed to place file: %v", err)
90 }
91 }
92 opts := Options{ID: 1234, Label: "TEST", BlockSize: blockSize}
93 if fixed == "Fixed" {
94 // Use a block count that is slightly higher than the minimum
95 opts.BlockCount = 67000
96 }
97 testWithFsck(t, rootInode, opts)
98 })
99 }
100 }
101}
102
103func TestLotsOfFilesFsck(t *testing.T) {
104 if os.Getenv("IN_KTEST") == "true" {
105 t.Skip("In ktest")
106 }
107 rootInode := Inode{
108 Attrs: AttrDirectory,
109 ModTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC),
110 }
111 for i := 0; i < (32*1024)-2; i++ {
112 rootInode.Children = append(rootInode.Children, &Inode{
113 Name: fmt.Sprintf("test%d", i),
114 Content: strings.NewReader("random test content"),
115 // Add some random attributes
116 Attrs: AttrHidden | AttrSystem,
117 // And a random ModTime
118 ModTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC),
119 })
120 }
121 testWithFsck(t, rootInode, Options{ID: 1234, Label: "TEST"})
122}