github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/image/fsck.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package image 5 6 import ( 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "os/exec" 12 "strconv" 13 "strings" 14 "sync" 15 16 "github.com/google/syzkaller/pkg/log" 17 "github.com/google/syzkaller/pkg/osutil" 18 ) 19 20 // Fsck runs fsckCmd against a file system image provided in r. It returns the 21 // fsck logs, whether the file system is clean and an error in case fsck could 22 // not be run. 23 func Fsck(r io.Reader, fsckCmd string) ([]byte, bool, error) { 24 // Write the image to a temporary file. 25 tempFile, err := os.CreateTemp("", "*.img") 26 if err != nil { 27 return nil, false, fmt.Errorf("failed to create temporary file: %w", err) 28 } 29 defer os.Remove(tempFile.Name()) 30 31 _, err = io.Copy(tempFile, r) 32 if err != nil { 33 return nil, false, fmt.Errorf("failed to write data to temporary file: %w", err) 34 } 35 36 if err := tempFile.Close(); err != nil { 37 return nil, false, fmt.Errorf("failed to close temporary file: %w", err) 38 } 39 40 osutil.SandboxChown(tempFile.Name()) 41 42 // And run the provided fsck command on it. 43 fsck := append(strings.Fields(fsckCmd), tempFile.Name()) 44 cmd := osutil.Command(fsck[0], fsck[1:]...) 45 if err := osutil.Sandbox(cmd, true, true); err != nil { 46 return nil, false, err 47 } 48 49 exitCode := 0 50 output, err := cmd.CombinedOutput() 51 if err != nil { 52 var exitError (*exec.ExitError) 53 ok := errors.As(err, &exitError) 54 if ok { 55 exitCode = exitError.ExitCode() 56 } else { 57 return nil, false, err 58 } 59 } 60 61 prefix := fsckCmd + " exited with status code " + strconv.Itoa(exitCode) + "\n" 62 return append([]byte(prefix), output...), exitCode == 0, nil 63 } 64 65 type FsckChecker struct { 66 mu sync.Mutex 67 exists map[string]bool 68 } 69 70 func (fc *FsckChecker) Exists(cmd string) bool { 71 fc.mu.Lock() 72 defer fc.mu.Unlock() 73 bin := strings.Fields(cmd)[0] 74 if ret, ok := fc.exists[bin]; ok { 75 return ret 76 } 77 if fc.exists == nil { 78 fc.exists = map[string]bool{} 79 } 80 _, err := exec.LookPath(bin) 81 found := err == nil 82 if !found { 83 log.Logf(0, "%s not found, images won't be checked", bin) 84 } 85 fc.exists[bin] = found 86 return found 87 }