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