|  | package fat32 | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "os" | 
|  | "os/exec" | 
|  | "strings" | 
|  | "testing" | 
|  | "time" | 
|  |  | 
|  | "source.monogon.dev/metropolis/cli/pkg/datafile" | 
|  | ) | 
|  |  | 
|  | func testWithFsck(t *testing.T, rootInode Inode, opts Options) { | 
|  | t.Helper() | 
|  | fsckPath, err := datafile.ResolveRunfile("external/com_github_dosfstools_dosfstools/fsck") | 
|  | if err != nil { | 
|  | t.Fatalf("unable to get path to fsck: %v", err) | 
|  | } | 
|  | testFile, err := os.CreateTemp("", "fat32-fsck-test") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer os.Remove(testFile.Name()) | 
|  | if err := WriteFS(testFile, rootInode, opts); err != nil { | 
|  | t.Fatalf("failed to write test FS: %v", err) | 
|  | } | 
|  | // Run fsck non-interactively (-n), disallow spaces in short file names (-S) | 
|  | // as well as perform deep verification (-V) | 
|  | // If the file system is OK (i.e. fsck does not want to fix it) it returns | 
|  | // 0, otherwise 1. | 
|  | fsckCmd := exec.Command(fsckPath, "-n", "-S", "-V", testFile.Name()) | 
|  | result, err := fsckCmd.CombinedOutput() | 
|  | if err != nil { | 
|  | t.Errorf("fsck failed: %v", string(result)) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestBasicFsck(t *testing.T) { | 
|  | if os.Getenv("IN_KTEST") == "true" { | 
|  | t.Skip("In ktest") | 
|  | } | 
|  | var largeString strings.Builder | 
|  | for i := 0; i < 16384; i++ { | 
|  | fmt.Fprintf(&largeString, "part%d", i) | 
|  | } | 
|  | // Test both common block sizes (512 and 4096 bytes) as well as the largest | 
|  | // supported one (32K) | 
|  | for _, blockSize := range []uint16{512, 4096, 32768} { | 
|  | for _, fixed := range []string{"", "Fixed"} { | 
|  | t.Run(fmt.Sprintf("BlockSize%d%v", blockSize, fixed), func(t *testing.T) { | 
|  | rootInode := Inode{ | 
|  | Attrs:      AttrDirectory, | 
|  | ModTime:    time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC), | 
|  | CreateTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC), | 
|  | } | 
|  | files := []struct { | 
|  | name    string | 
|  | path    string | 
|  | content string | 
|  | }{ | 
|  | {"FileInRoot", "test1.txt", "test1 content"}, | 
|  | {"LongFileInRoot", "verylongtest1.txt", "test1 content long"}, | 
|  | {"LongPath", "test1/test2/test3/test4/longdirname.ext/hello", "long path test content"}, | 
|  | {"LargeFile", "test1/largefile.txt", largeString.String()}, | 
|  | } | 
|  | for _, c := range files { | 
|  | err := rootInode.PlaceFile(c.path, strings.NewReader(c.content)) | 
|  | if err != nil { | 
|  | t.Errorf("failed to place file: %v", err) | 
|  | } | 
|  | } | 
|  | opts := Options{ID: 1234, Label: "TEST", BlockSize: blockSize} | 
|  | if fixed == "Fixed" { | 
|  | // Use a block count that is slightly higher than the minimum | 
|  | opts.BlockCount = 67000 | 
|  | } | 
|  | testWithFsck(t, rootInode, opts) | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestLotsOfFilesFsck(t *testing.T) { | 
|  | if os.Getenv("IN_KTEST") == "true" { | 
|  | t.Skip("In ktest") | 
|  | } | 
|  | rootInode := Inode{ | 
|  | Attrs:   AttrDirectory, | 
|  | ModTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC), | 
|  | } | 
|  | for i := 0; i < (32*1024)-2; i++ { | 
|  | rootInode.Children = append(rootInode.Children, &Inode{ | 
|  | Name:    fmt.Sprintf("test%d", i), | 
|  | Content: strings.NewReader("random test content"), | 
|  | // Add some random attributes | 
|  | Attrs: AttrHidden | AttrSystem, | 
|  | // And a random ModTime | 
|  | ModTime: time.Date(2022, 03, 04, 5, 6, 7, 8, time.UTC), | 
|  | }) | 
|  | } | 
|  | testWithFsck(t, rootInode, Options{ID: 1234, Label: "TEST"}) | 
|  | } |