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  }