// Copyright 2024 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package image_test import ( "fmt" "io" "os" "path/filepath" "strings" "testing" . "github.com/google/syzkaller/pkg/image" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" ) // To get maximum test coverage here, install the following Debian packages: // dosfstools e2fsprogs btrfs-progs util-linux f2fs-tools jfsutils util-linux // dosfstools ocfs2-tools reiserfsprogs xfsprogs erofs-utils exfatprogs // gfs2-utils. const corruptedFs = "IAmACorruptedFs" func TestFsck(t *testing.T) { target, err := prog.GetTarget(targets.Linux, targets.AMD64) if err != nil { t.Fatal(err) } fsckChecker := FsckChecker{} // Use the images generated by syz-imagegen as a collection of clean file systems. cleanFsProgs, err := filepath.Glob(filepath.Join("..", "sys", "linux", "test", "syz_mount_image_*_0")) if err != nil { t.Fatalf("directory read failed: %v", err) } for _, file := range cleanFsProgs { sourceProg, err := os.ReadFile(file) if err != nil { t.Fatal(err) } p, err := target.Deserialize(sourceProg, prog.NonStrict) if err != nil { t.Fatalf("failed to deserialize %s: %s", file, err) } p.ForEachAsset(func(name string, typ prog.AssetType, r io.Reader, c *prog.Call) { if c.Meta.Attrs.Fsck == "" { return } fsckCmd := c.Meta.Attrs.Fsck // Tolerate missing fsck commands except during CI runs. skip := !fsckChecker.Exists(fsckCmd) && os.Getenv("CI") == "" fsName := strings.TrimPrefix(c.Meta.Name, "syz_mount_image$") // Check that the file system in the image is detected as clean. t.Run(fmt.Sprintf("clean %s", fsName), func(t *testing.T) { if skip { t.Skipf("%s not available", fsckCmd) } logs, isClean, err := Fsck(r, fsckCmd) if err != nil { t.Fatalf("failed to run fsck %s", err) } if !isClean { t.Fatalf("%s should exit 0 on a clean file system %s", fsckCmd, string(logs)) } }) // And use the same fsck command on a dummy fs to make sure that fails. t.Run(fmt.Sprintf("corrupt %s", fsName), func(t *testing.T) { if skip { t.Skipf("%s not available", fsckCmd) } logs, isClean, err := Fsck(strings.NewReader(corruptedFs), fsckCmd) if err != nil { t.Fatalf("failed to run fsck %s", err) } if isClean { t.Fatalf("%s shouldn't exit 0 on a corrupt file system %s", fsckCmd, string(logs)) } }) }) } }