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