github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-imagegen/imagegen.go (about)

     1  // Copyright 2018 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  // As we use syscall package:
     5  //go:build linux
     6  
     7  // syz-imagegen generates sys/linux/test/syz_mount_image_* test files.
     8  // It requires the following packages to be installed:
     9  //
    10  //	f2fs-tools xfsprogs reiserfsprogs gfs2-utils ocfs2-tools genromfs erofs-utils makefs udftools
    11  //	mtd-utils nilfs-tools squashfs-tools genisoimage jfsutils exfat-utils ntfs-3g hfsprogs
    12  //	bcachefs-tools.
    13  package main
    14  
    15  import (
    16  	"bufio"
    17  	"bytes"
    18  	"encoding/binary"
    19  	"errors"
    20  	"flag"
    21  	"fmt"
    22  	"hash/crc32"
    23  	"os"
    24  	"os/exec"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strings"
    28  	"syscall"
    29  	"time"
    30  	"unsafe"
    31  
    32  	"github.com/google/syzkaller/pkg/image"
    33  	"github.com/google/syzkaller/pkg/osutil"
    34  	"github.com/google/syzkaller/pkg/tool"
    35  	"github.com/google/syzkaller/prog"
    36  	_ "github.com/google/syzkaller/sys"
    37  	"github.com/google/syzkaller/sys/targets"
    38  )
    39  
    40  // FileSystem represents one file system.
    41  // Each FileSystem produces multiple images, see MkfsFlagCombinations and Image type.
    42  type FileSystem struct {
    43  	// Name of the file system. Needs to match syz_mount_image$NAME name.
    44  	Name string
    45  	// Imagegen autodetects size for images starting from MinSize and then repeatedly doubling it if mkfs fails.
    46  	MinSize int
    47  	// Don't populate this image with files (can't mount read-write).
    48  	ReadOnly bool
    49  	// These flags are always appended to mkfs as is.
    50  	MkfsFlags []string
    51  	// Generate images for all possible permutations of these flag combinations.
    52  	MkfsFlagCombinations [][]string
    53  	// Custom mkfs invocation, if nil then mkfs.name is invoked in a standard way.
    54  	Mkfs func(img *Image) error
    55  }
    56  
    57  // nolint:lll
    58  var fileSystems = []FileSystem{
    59  	{
    60  		Name:      "f2fs",
    61  		MinSize:   64 << 20,
    62  		MkfsFlags: []string{"-e cold"},
    63  		MkfsFlagCombinations: [][]string{
    64  			{"-a 0", "-a 1"},
    65  			{"-s 1", "-s 2"},
    66  			{
    67  				"",
    68  				"-O encrypt",
    69  				"-O extra_attr",
    70  				"-O extra_attr -O flexible_inline_xattr -O inode_checksum -O inode_crtime -O project_quota",
    71  			},
    72  		},
    73  	},
    74  	{
    75  		Name:    "btrfs",
    76  		MinSize: 16 << 20,
    77  		MkfsFlagCombinations: [][]string{
    78  			{"", "-M"},
    79  			{"", "-K"},
    80  			{"--csum crc32c", "--csum xxhash", "--csum sha256", "--csum blake2"},
    81  			{"--nodesize 4096 -O mixed-bg", "-O extref", "-O raid56", "-O no-holes", "-O raid1c34"},
    82  		},
    83  	},
    84  	{
    85  		Name:      "vfat",
    86  		MinSize:   64 << 10,
    87  		MkfsFlags: []string{"-n", "SYZKALLER"},
    88  		MkfsFlagCombinations: [][]string{
    89  			{"", "-a -I"},
    90  			{"", "-h 3 -f 4"},
    91  			{"-s 1", "-s 8", "-s 64"},
    92  			{
    93  				"-F 12 -r 64 -S 512",
    94  				"-F 12 -r 64 -S 2048 -A",
    95  				"-F 16 -r 112 -S 512",
    96  				"-F 32 -r 768 -S 512",
    97  				"-F 32 -r 768 -S 2048 -A",
    98  			},
    99  		},
   100  	},
   101  	{
   102  		Name:      "msdos",
   103  		MinSize:   64 << 10,
   104  		MkfsFlags: []string{"-n", "SYZKALLER"},
   105  		MkfsFlagCombinations: [][]string{
   106  			{"", "-a -I"},
   107  			{"", "-h 3 -f 4"},
   108  			{"-s 1", "-s 8", "-s 64"},
   109  			{
   110  				"-F 12 -r 64 -S 512",
   111  				"-F 12 -r 64 -S 2048 -A",
   112  				"-F 16 -r 112 -S 512",
   113  				"-F 32 -r 768 -S 512",
   114  				"-F 32 -r 768 -S 2048 -A",
   115  			},
   116  		},
   117  	},
   118  	{
   119  		Name:      "exfat",
   120  		MinSize:   128 << 10,
   121  		MkfsFlags: []string{"-i", "0x12341234"},
   122  		MkfsFlagCombinations: [][]string{
   123  			{"", "-p 3"},
   124  		},
   125  	},
   126  	{
   127  		Name:      "bfs",
   128  		MinSize:   4 << 10,
   129  		ReadOnly:  true, // creating files fails with ENOPERM
   130  		MkfsFlags: []string{"-V", "syzkal", "-F", "syzkal"},
   131  		MkfsFlagCombinations: [][]string{
   132  			{"-N 48", "-N 127", "-N 512"},
   133  		},
   134  	},
   135  	{
   136  		Name:      "xfs",
   137  		MinSize:   16 << 20,
   138  		MkfsFlags: []string{"-l", "internal", "--unsupported"},
   139  		MkfsFlagCombinations: [][]string{
   140  			// Most XFS options are inter-dependent and total number of combinations is huge,
   141  			// so we enumerate some combinations that don't produce errors.
   142  			{
   143  				"-b size=512 -i size=256  -d agcount=2 -m crc=0 -m finobt=0 -m rmapbt=0 -m reflink=0 -i sparse=0 -i maxpct=25  -i attr=1 -i projid32bit=0 -l lazy-count=0",
   144  				"-b size=2k  -i size=1024 -d agcount=2 -m crc=0 -m finobt=0 -m rmapbt=0 -m reflink=0 -i sparse=0 -i maxpct=5   -i attr=1 -i projid32bit=0 -l lazy-count=1",
   145  				"-b size=4k  -i size=2048 -d agcount=4 -m crc=0 -m finobt=0 -m rmapbt=0 -m reflink=0 -i sparse=0 -i maxpct=90  -i attr=2 -i projid32bit=1 -l lazy-count=0",
   146  				"-b size=1k  -i size=512  -d agcount=2 -m crc=1 -m finobt=0 -m rmapbt=0 -m reflink=0 -i sparse=0 -i maxpct=20  -i attr=2 -i projid32bit=1 -l lazy-count=1",
   147  				"-b size=2k  -i size=1024 -d agcount=4 -m crc=1 -m finobt=1 -m rmapbt=1 -m reflink=1 -i sparse=0 -i maxpct=3   -i attr=2 -i projid32bit=1 -l lazy-count=1",
   148  				"-b size=4k  -i size=2048 -d agcount=1 -m crc=1 -m finobt=0 -m rmapbt=1 -m reflink=0 -i sparse=0 -i maxpct=100 -i attr=2 -i projid32bit=1 -l lazy-count=1",
   149  				"-b size=1k  -i size=512  -d agcount=2 -m crc=1 -m finobt=0 -m rmapbt=0 -m reflink=0 -i sparse=1 -i maxpct=99  -i attr=2 -i projid32bit=1 -l lazy-count=1",
   150  				"-b size=2k  -i size=1024 -d agcount=1 -m crc=1 -m finobt=1 -m rmapbt=1 -m reflink=1 -i sparse=1 -i maxpct=50  -i attr=2 -i projid32bit=1 -l lazy-count=1",
   151  				"-b size=4k  -i size=1024 -d agcount=1 -m crc=1 -m finobt=1 -m rmapbt=0 -m reflink=1 -i sparse=1 -i maxpct=10  -i attr=2 -i projid32bit=1 -l lazy-count=1",
   152  			},
   153  			{"-l sunit=16", "-l sunit=64", "-l sunit=128", "-l su=8k"},
   154  		},
   155  	},
   156  	{
   157  		Name:    "minix",
   158  		MinSize: 16 << 10,
   159  		MkfsFlagCombinations: [][]string{
   160  			{
   161  				"-1 -n 14",
   162  				"-1 -n 30",
   163  				"-2 -n 14",
   164  				"-2 -n 30",
   165  				"-3 -n 60",
   166  			},
   167  			{"-i 16", "-i 64", "-i 1024"},
   168  		},
   169  	},
   170  	{
   171  		Name:      "reiserfs",
   172  		MinSize:   4 << 20,
   173  		ReadOnly:  true, // mounting this crashes my host kernel
   174  		MkfsFlags: []string{"-f", "-f", "-l", "syzkaller"},
   175  		MkfsFlagCombinations: [][]string{
   176  			{"-b 4096", "-b 8192"},
   177  			{"-h r5", "-h rupasov", "-h tea"},
   178  			{"--format 3.5", "--format 3.6 -u 12312312-1233-1233-1231-123413412412"},
   179  			{
   180  				"-s 513",
   181  				"-s 8193 -t 128",
   182  				"-s 8193 -t 1024",
   183  				"-s 15749 -t 128",
   184  				"-s 15749 -t 1024",
   185  			},
   186  		},
   187  	},
   188  	{
   189  		Name:      "jfs",
   190  		MinSize:   16 << 20,
   191  		MkfsFlags: []string{"-q"},
   192  		MkfsFlagCombinations: [][]string{
   193  			{"", "-s 1M"},
   194  			{"", "-O"},
   195  		},
   196  	},
   197  	{
   198  		Name:      "ntfs",
   199  		MinSize:   1 << 20,
   200  		MkfsFlags: []string{"-f", "-F", "-L", "syzkaller"},
   201  		MkfsFlagCombinations: [][]string{
   202  			{
   203  				"-s 256 -c 2048",
   204  				"-s 512 -c 1024",
   205  				"-s 512 -c 4096",
   206  				"-s 1024 -c 4096",
   207  				"-s 1024 -c 65536",
   208  				"-s 2048 -c 2048",
   209  				"-s 2048 -c 4096",
   210  				"-s 4096 -c 4096",
   211  				"-s 4096 -c 131072",
   212  			},
   213  			{"", "-I"},
   214  		},
   215  	},
   216  	{
   217  		Name:      "ntfs3",
   218  		MinSize:   1 << 20,
   219  		MkfsFlags: []string{"-f", "-F", "-L", "syzkaller"},
   220  		MkfsFlagCombinations: [][]string{
   221  			{
   222  				"-s 512 -c 1024",
   223  				"-s 512 -c 4096",
   224  				"-s 1024 -c 4096",
   225  				"-s 1024 -c 65536",
   226  				"-s 2048 -c 2048",
   227  				"-s 2048 -c 4096",
   228  				"-s 4096 -c 4096",
   229  				"-s 4096 -c 131072",
   230  			},
   231  			{"", "-I"},
   232  		},
   233  		Mkfs: func(img *Image) error {
   234  			_, err := runCmd("mkfs.ntfs", append(img.flags, img.disk)...)
   235  			return err
   236  		},
   237  	},
   238  	{
   239  		Name:      "ext4",
   240  		MinSize:   64 << 10,
   241  		MkfsFlags: []string{"-L", "syzkaller", "-U", "clear", "-E", "test_fs"},
   242  		MkfsFlagCombinations: [][]string{
   243  			{"-t ext2", "-t ext3", "-t ext4"},
   244  			// Total number of combinations is too large and there are lots of dependencies between flags,
   245  			// so we create just few permutations generated with fair dice rolls.
   246  			// TODO: We also need to give some combination of -E encoding=utf8/utf8-12.1 and -E encoding_flags=strict,
   247  			// but mounting such fs on my host fails with "Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE".
   248  			{
   249  				"-b 1024 -I 128 -E lazy_itable_init=0 -E num_backup_sb=0 -E packed_meta_blocks=0 -O ^64bit -O extents -O ^bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O mmp -O quota -O ^resize_inode -O ^sparse_super -O ^uninit_bg -O ^verity -j -J size=1024",
   250  				"-b 1024 -I 256 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O ^verity",
   251  				"-b 1024 -I 1024 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O ^verity -j -J size=1024",
   252  				"-b 1024 -I 128 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O ^inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O ^verity",
   253  				"-b 1024 -I 256 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O ^verity",
   254  				"-b 1024 -I 256 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O ^verity -j -J size=1024",
   255  				"-b 1024 -I 512 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O ^verity",
   256  				"-b 2048 -I 128 -E lazy_itable_init=0 -E num_backup_sb=0 -E packed_meta_blocks=0 -O ^64bit -O extents -O ^bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O mmp -O quota -O ^resize_inode -O ^sparse_super -O ^uninit_bg -O ^verity",
   257  				"-b 2048 -I 256 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O ^verity -j -J size=1024",
   258  				"-b 2048 -I 1024 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O ^verity",
   259  				"-b 2048 -I 128 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O ^inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O ^verity",
   260  				"-b 2048 -I 256 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O ^verity -j -J size=1024",
   261  				"-b 2048 -I 256 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O ^verity",
   262  				"-b 2048 -I 512 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O ^encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O ^verity",
   263  				"-b 4096 -I 128 -E lazy_itable_init=0 -E num_backup_sb=0 -E packed_meta_blocks=0 -O ^64bit -O extents -O ^bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O mmp -O quota -O ^resize_inode -O ^sparse_super -O ^uninit_bg -O ^verity -j -J size=1024",
   264  				"-b 4096 -I 256 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O ^inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O verity",
   265  				"-b 4096 -I 1024 -E lazy_itable_init=0 -E num_backup_sb=1 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O ^dir_nlink -O ^ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O flex_bg -O ^huge_file -O inline_data -O large_dir -O ^metadata_csum -O meta_bg -O ^mmp -O quota -O ^resize_inode -O ^sparse_super -O uninit_bg -O ^verity",
   266  				"-b 4096 -I 128 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O ^inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O verity -j -J size=1024",
   267  				"-b 4096 -I 256 -E lazy_itable_init=1 -E num_backup_sb=0 -E packed_meta_blocks=1 -O  64bit -O extents -O bigalloc -O ^dir_index -O dir_nlink -O ea_inode -O ^encrypt -O ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O mmp -O ^quota -O resize_inode -O sparse_super -O ^uninit_bg -O ^verity",
   268  				"-b 4096 -I 256 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O ^encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O verity -j -J size=1024",
   269  				"-b 4096 -I 512 -E lazy_itable_init=1 -E num_backup_sb=1 -E packed_meta_blocks=0 -O ^64bit -O ^extents -O ^bigalloc -O dir_index -O ^dir_nlink -O ea_inode -O encrypt -O ^ext_attr -O ^extra_isize -O ^flex_bg -O huge_file -O inline_data -O ^large_dir -O ^metadata_csum -O ^meta_bg -O ^mmp -O ^quota -O ^resize_inode -O sparse_super2 -O uninit_bg -O ^verity",
   270  			},
   271  		},
   272  	},
   273  	{
   274  		Name:      "gfs2",
   275  		MinSize:   16 << 20,
   276  		ReadOnly:  true, // mounting this crashes my host kernel
   277  		MkfsFlags: []string{"-O", "-t", "syz:syz"},
   278  		MkfsFlagCombinations: [][]string{
   279  			{
   280  				// Lots of combinations lead to huge images that don't fit into 4MB encodingexec buffer.
   281  				"-b 1024 -o sunit=1024 -o swidth=1024 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   282  				"-b 1024 -o sunit=1024 -o swidth=1024 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   283  				"-b 1024 -o sunit=1024 -o swidth=1024 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   284  				"-b 1024 -o sunit=1024 -o swidth=1024 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   285  				"-b 1024 -o sunit=4096 -o swidth=8192 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   286  				"-b 1024 -o sunit=4096 -o swidth=8192 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   287  				"-b 1024 -o sunit=4096 -o swidth=8192 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   288  				"-b 1024 -o sunit=4096 -o swidth=8192 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   289  				"-b 2048 -o sunit=2048 -o swidth=4096 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   290  				"-b 2048 -o sunit=2048 -o swidth=4096 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   291  				"-b 2048 -o sunit=2048 -o swidth=4096 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   292  				"-b 2048 -o sunit=2048 -o swidth=4096 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   293  				"-b 2048 -o sunit=4096 -o swidth=4096 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   294  				"-b 2048 -o sunit=4096 -o swidth=4096 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   295  				"-b 2048 -o sunit=4096 -o swidth=4096 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   296  				"-b 2048 -o sunit=4096 -o swidth=4096 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   297  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   298  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   299  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 1M -j 2 -J 16 -o align=0 -p lock_dlm",
   300  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 1M -j 2 -J 16 -o align=1 -p lock_nolock",
   301  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   302  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   303  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 4M -j 2 -J 16 -o align=0 -p lock_dlm",
   304  				"-b 4096 -o sunit=4096 -o swidth=4096 -c 4M -j 2 -J 16 -o align=1 -p lock_nolock",
   305  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 1M -j 1 -J 8 -o align=0 -p lock_dlm",
   306  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 1M -j 1 -J 8 -o align=1 -p lock_nolock",
   307  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 1M -j 2 -J 16 -o align=0 -p lock_dlm",
   308  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 1M -j 2 -J 16 -o align=1 -p lock_nolock",
   309  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 4M -j 1 -J 8 -o align=0 -p lock_dlm",
   310  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 4M -j 1 -J 8 -o align=1 -p lock_nolock",
   311  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 4M -j 2 -J 16 -o align=0 -p lock_dlm",
   312  				"-b 4096 -o sunit=8192 -o swidth=16384 -c 4M -j 2 -J 16 -o align=1 -p lock_nolock",
   313  			},
   314  		},
   315  	},
   316  	{
   317  		Name:     "ocfs2",
   318  		MinSize:  8 << 20,
   319  		ReadOnly: true, // mounting this crashes my host kernel
   320  		MkfsFlagCombinations: [][]string{
   321  			{"-b 512", "-b 4096"},
   322  			{"-C 4K", "-C 16K", "-C 1M"},
   323  			{"-J block32", "-J block64"},
   324  			{"-T mail -N 1 -M local", "-T datafiles -N 2 -M local", "-T vmstore -N 2 -M cluster"},
   325  			{"", "--fs-features backup-super,sparse,unwritten,inline-data,extended-slotmap,metaecc,refcount,xattr,usrquota,grpquota,indexed-dirs,discontig-bg"},
   326  		},
   327  	},
   328  	{
   329  		Name:    "cramfs",
   330  		MinSize: 1 << 10,
   331  		// The file system is read-only and requires a root directory at creation time.
   332  		ReadOnly:  true,
   333  		MkfsFlags: []string{},
   334  		MkfsFlagCombinations: [][]string{
   335  			{"-b 4096", "-b 8192"},
   336  			{"-N big", "-N little"},
   337  		},
   338  		Mkfs: func(img *Image) error {
   339  			_, err := runCmd("mkfs.cramfs", append(img.flags, img.templateDir, img.disk)...)
   340  			return err
   341  		},
   342  	},
   343  	{
   344  		Name:    "romfs",
   345  		MinSize: 1 << 10,
   346  		// The file system is read-only and requires a root directory at creation time.
   347  		ReadOnly: true,
   348  		MkfsFlagCombinations: [][]string{
   349  			{"-a 16", "-a 256"},
   350  		},
   351  		Mkfs: func(img *Image) error {
   352  			_, err := runCmd("genromfs", append(img.flags, "-f", img.disk, "-d", img.templateDir)...)
   353  			return err
   354  		},
   355  	},
   356  	{
   357  		Name:    "erofs",
   358  		MinSize: 1 << 10,
   359  		// The file system is read-only and requires a root directory at creation time.
   360  		ReadOnly:  true,
   361  		MkfsFlags: []string{"-T1000"},
   362  		MkfsFlagCombinations: [][]string{
   363  			{"-z lz4", "-z lz4hc,1", "-z lz4hc,9"},
   364  			{"-x 1", "-x 2"},
   365  			{"", "-E legacy-compress"},
   366  		},
   367  		Mkfs: func(img *Image) error {
   368  			_, err := runCmd("mkfs.erofs", append(img.flags, img.disk, img.templateDir)...)
   369  			return err
   370  		},
   371  	},
   372  	{
   373  		Name:    "efs",
   374  		MinSize: 1 << 10,
   375  		// The file system is read-only and requires a root directory at creation time.
   376  		ReadOnly:  true,
   377  		MkfsFlags: []string{"-M", "65536"},
   378  		MkfsFlagCombinations: [][]string{
   379  			{
   380  				"-t ffs -B big    -S 128  -o bsize=4k,version=1,optimization=space",
   381  				"-t ffs -B big    -S 512  -o bsize=8k,version=1,optimization=time",
   382  				"-t ffs -B big    -S 2048 -o bsize=8k,version=1,optimization=space",
   383  				"-t ffs -B little -S 128  -o bsize=4k,version=1,optimization=time",
   384  				"-t ffs -B little -S 512  -o bsize=4k,version=1,optimization=space",
   385  				"-t ffs -B little -S 2048 -o bsize=8k,version=1,optimization=time",
   386  				"-t ffs -B big    -S 128  -o bsize=4k,version=2,optimization=space",
   387  				"-t ffs -B big    -S 512  -o bsize=8k,version=2,optimization=time",
   388  				"-t ffs -B big    -S 2048 -o bsize=8k,version=2,optimization=space",
   389  				"-t ffs -B little -S 128  -o bsize=4k,version=2,optimization=time",
   390  				"-t ffs -B little -S 512  -o bsize=4k,version=2,optimization=space",
   391  				"-t ffs -B little -S 2048 -o bsize=8k,version=2,optimization=time",
   392  				"-t cd9660",
   393  				"-t cd9660 -o rockridge",
   394  			},
   395  		},
   396  		Mkfs: func(img *Image) error {
   397  			_, err := runCmd("makefs", append(img.flags, img.disk, img.templateDir)...)
   398  			return err
   399  		},
   400  	},
   401  	{
   402  		Name:      "udf",
   403  		MinSize:   64 << 10,
   404  		MkfsFlags: []string{"-u", "1234567812345678"},
   405  		MkfsFlagCombinations: [][]string{
   406  			{"-b 512", "-b 1024", "-b 4096"},
   407  			{
   408  				"-m hd -r 1.01",
   409  				"-m hd -r 1.01 --ad=short",
   410  				"-m hd -r 1.50 --ad=long",
   411  				"-m hd -r 2.01",
   412  				"-m hd -r 2.01 --space=unallocbitmap",
   413  				"-m mo -r 1.01",
   414  				"-m mo -r 1.01  --ad=long",
   415  				"-m mo -r 1.50 --space=unalloctable",
   416  				"-m mo -r 1.50 --space=unallocbitmap",
   417  				"-m mo -r 2.01 --noefe --ad=short",
   418  				"-m cdrw -r 1.50",
   419  				"-m cdrw -r 1.50 --space=unalloctable --ad=long",
   420  				"-m cdrw -r 2.01",
   421  				"-m dvdrw -r 1.50 --space=unallocbitmap --ad=short",
   422  				"-m dvdrw -r 2.01 --sparspace=512 --space=unalloctable --noefe",
   423  				"-m dvdrw -r 2.01 --sparspace=512",
   424  				"-m dvdram -r 1.50  --ad=long",
   425  				"-m dvdram -r 2.01 ",
   426  				"-m dvdram -r 2.01 --space=unallocbitmap  --ad=long",
   427  			},
   428  		},
   429  	},
   430  	{
   431  		Name:      "jffs2",
   432  		MinSize:   1 << 10,
   433  		ReadOnly:  true,
   434  		MkfsFlags: []string{"--squash", "--faketime", "--with-xattr"},
   435  		MkfsFlagCombinations: [][]string{
   436  			{"--pagesize 4096", "--pagesize 8192"},
   437  			{"--little-endian", "--big-endian"},
   438  			{"--compression-mode=none", "--compression-mode=size"},
   439  		},
   440  		Mkfs: func(img *Image) error {
   441  			_, err := runCmd("mkfs.jffs2", append(img.flags, "-o", img.disk, "--root", img.templateDir)...)
   442  			return err
   443  		},
   444  	},
   445  	{
   446  		Name:    "nilfs2",
   447  		MinSize: 1 << 20,
   448  		MkfsFlagCombinations: [][]string{
   449  			{"-b 1024", "-b 2048", "-b 4096"},
   450  			{"-B 16", "-B 64", "-B 512"},
   451  			{"-O none", "-O block_count"},
   452  		},
   453  	},
   454  	{
   455  		Name:     "squashfs",
   456  		MinSize:  1 << 10,
   457  		ReadOnly: true,
   458  		MkfsFlagCombinations: [][]string{
   459  			{"-comp gzip -b 4k", "-comp lzo -b 16k", "-comp xz -b 1M"},
   460  			{"", "-noI -noX", "-noI -noD -noF -noX"},
   461  			{"-no-fragments", "-always-use-fragments -nopad"},
   462  		},
   463  		Mkfs: func(img *Image) error {
   464  			os.Remove(img.disk)
   465  			_, err := runCmd("mksquashfs", append([]string{img.templateDir, img.disk}, img.flags...)...)
   466  			return err
   467  		},
   468  	},
   469  	{
   470  		Name:      "iso9660",
   471  		MinSize:   1 << 10,
   472  		ReadOnly:  true,
   473  		MkfsFlags: []string{"-abstract", "file1", "-biblio", "file2", "-copyright", "file3", "-publisher", "syzkaller"},
   474  		MkfsFlagCombinations: [][]string{
   475  			{"", "-J", "-J -udf"},
   476  			{"-pad", "-no-pad"},
   477  			{"", "-hfs", "-apple -r"},
   478  		},
   479  		Mkfs: func(img *Image) error {
   480  			_, err := runCmd("genisoimage", append(img.flags, "-o", img.disk, img.templateDir)...)
   481  			return err
   482  		},
   483  	},
   484  	{
   485  		Name:    "hfs",
   486  		MinSize: 16 << 10,
   487  		MkfsFlagCombinations: [][]string{
   488  			{"", "-P"},
   489  			{"", "-c a=1024,b=512,c=128,d=256"},
   490  		},
   491  	},
   492  	{
   493  		Name:    "hfsplus",
   494  		MinSize: 512 << 10,
   495  		MkfsFlagCombinations: [][]string{
   496  			{"", "-P"},
   497  			{"", "-s"},
   498  			{"-b 512", "-b 1024", "-b 2048"},
   499  		},
   500  	},
   501  	{
   502  		Name:    "bcachefs",
   503  		MinSize: 512 << 10,
   504  		MkfsFlagCombinations: [][]string{
   505  			{"", "--encrypted --no_passphrase"},
   506  			{"", "--compression=lz4"},
   507  			{"", "--data_checksum=none --metadata_checksum=none"},
   508  		},
   509  	},
   510  	{
   511  		Name:     parttable,
   512  		MinSize:  1 << 20,
   513  		ReadOnly: true,
   514  		MkfsFlagCombinations: [][]string{{
   515  			// You can test/explore these commands with:
   516  			// $ rm -f disk && touch disk && fallocate -l 16M disk && fdisk disk
   517  			"g n 1 34 47 n 2 48 96 n 3 97 2013 t 1 uefi t 2 linux t 3 swap w",
   518  			"g n 1 100 200 n 2 50 80 n 3 1000 1900 M a c r x n 1 syzkaller A 1 r w",
   519  			"g n 3 200 300 n 7 400 500 n 2 600 700 x l 700 r w",
   520  			"G n 1 16065 16265 w",
   521  			"G n 16 20000 30000 i w",
   522  			"o n p 1 2048 4096 n p 3 4097 4100 n e 2 4110 4200 a 1 t 2 a5 t 3 uefi b y x 1 e r w",
   523  			"o n p 1 2048 3071 n e 3 3072 32767 n 6200 7999 n 10240 20000 w",
   524  			"s c 1 a 2 w",
   525  		}},
   526  		Mkfs: func(img *Image) error {
   527  			cmd := exec.Command("fdisk", "--noauto-pt", "--color=always", img.disk)
   528  			cmd.Stdin = strings.NewReader(strings.Join(img.flags, "\n"))
   529  			output, err := osutil.Run(10*time.Minute, cmd)
   530  			if err != nil {
   531  				return err
   532  			}
   533  			// It's hard to understand if any of the commands fail,
   534  			// fdisk does not exit with an error and print assorted error messages.
   535  			// So instead we run it with --color=always and grep for red color marker
   536  			// in the output (ESC[31m).
   537  			if bytes.Contains(output, []byte{0x1b, 0x5b, 0x33, 0x31, 0x6d}) {
   538  				return errors.New(string(output))
   539  			}
   540  			return err
   541  		},
   542  	},
   543  }
   544  
   545  const (
   546  	syzMountImage    = "syz_mount_image"
   547  	syzReadPartTable = "syz_read_part_table"
   548  	parttable        = "parttable"
   549  )
   550  
   551  func (fs FileSystem) filePrefix() string {
   552  	if fs.Name == parttable {
   553  		return syzReadPartTable
   554  	}
   555  	return syzMountImage + "_" + fs.Name
   556  }
   557  
   558  // Image represents one image we generate for a file system.
   559  type Image struct {
   560  	target      *prog.Target
   561  	fs          FileSystem
   562  	flags       []string // mkfs flags
   563  	index       int      // index within the file system
   564  	size        int      // image size (autodetected starting from fs.MinSize)
   565  	hash        uint32   // crc32 hash of the resulting image to detect duplicates
   566  	disk        string   // disk image file name
   567  	templateDir string   // name of a directory with contents for the file system (shared across all images)
   568  	done        chan error
   569  }
   570  
   571  func (img *Image) String() string {
   572  	size := fmt.Sprintf("%vKB", img.size>>10)
   573  	if img.size >= 1<<20 {
   574  		size = fmt.Sprintf("%vMB", img.size>>20)
   575  	}
   576  	return fmt.Sprintf("#%02v: mkfs.%v[%5v] %v", img.index, img.fs.Name, size, img.flags)
   577  }
   578  
   579  var errShutdown = errors.New("shutdown")
   580  
   581  func main() {
   582  	var (
   583  		flagList      = flag.Bool("list", false, "list supported file systems and exit")
   584  		flagVerbose   = flag.Bool("v", false, "print successfully created images")
   585  		flagDebug     = flag.Bool("debug", false, "print lots of debugging output")
   586  		flagPopulate  = flag.String("populate", "", "populate the specified image with files (for internal use)")
   587  		flagKeepImage = flag.Bool("keep", false, "save disk images as .img files")
   588  		flagFS        = flag.String("fs", "", "comma-separated list of filesystems to generate, all if empty")
   589  	)
   590  	flag.Parse()
   591  	if *flagDebug {
   592  		*flagVerbose = true
   593  	}
   594  	if *flagPopulate != "" {
   595  		if err := populate(*flagPopulate, *flagFS); err != nil {
   596  			tool.Fail(err)
   597  		}
   598  		return
   599  	}
   600  	target, err := prog.GetTarget(targets.Linux, targets.AMD64)
   601  	if err != nil {
   602  		tool.Fail(err)
   603  	}
   604  	addEmptyImages(target)
   605  	images, err := generateImages(target, *flagFS, *flagList)
   606  	if err != nil {
   607  		tool.Fail(err)
   608  	}
   609  	if *flagList {
   610  		return
   611  	}
   612  	// Create a single template dir for file systems that need the root dir at creation time.
   613  	templateDir, err := os.MkdirTemp("", "syz-imagegen")
   614  	if err != nil {
   615  		tool.Fail(err)
   616  	}
   617  	defer os.RemoveAll(templateDir)
   618  	if err := populateDir(templateDir); err != nil {
   619  		tool.Fail(err)
   620  	}
   621  	shutdown := make(chan struct{})
   622  	osutil.HandleInterrupts(shutdown)
   623  	procs := runtime.NumCPU()
   624  	requests := make(chan *Image, procs)
   625  	go func() {
   626  		for _, img := range images {
   627  			img.templateDir = templateDir
   628  			requests <- img
   629  		}
   630  		close(requests)
   631  	}()
   632  	for p := 0; p < procs; p++ {
   633  		go func() {
   634  			for img := range requests {
   635  				select {
   636  				case <-shutdown:
   637  					img.done <- errShutdown
   638  				default:
   639  					img.done <- img.generate()
   640  				}
   641  			}
   642  		}()
   643  	}
   644  	printResults(images, shutdown, *flagKeepImage, *flagVerbose)
   645  }
   646  
   647  func addEmptyImages(target *prog.Target) {
   648  	// Since syz_mount_image calls are no_generate we need to add at least some
   649  	// empty seeds for all the filesystems.
   650  	have := make(map[string]bool)
   651  	for _, fs := range fileSystems {
   652  		have[fs.Name] = true
   653  	}
   654  	for _, call := range target.Syscalls {
   655  		if call.CallName != syzMountImage {
   656  			continue
   657  		}
   658  		name := strings.TrimPrefix(call.Name, syzMountImage+"$")
   659  		if have[name] {
   660  			continue
   661  		}
   662  		fileSystems = append(fileSystems, FileSystem{
   663  			Name:      name,
   664  			MinSize:   64 << 10,
   665  			ReadOnly:  true,
   666  			MkfsFlags: []string{"fake image"},
   667  			Mkfs: func(img *Image) error {
   668  				// Fill the image with unique 4-byte values.
   669  				// This allows hints mutation to easily guess magic numbers and checksums.
   670  				f, err := os.Create(img.disk)
   671  				if err != nil {
   672  					return err
   673  				}
   674  				defer f.Close()
   675  				buf := bufio.NewWriter(f)
   676  				defer buf.Flush()
   677  				for i := uint32(0); i < uint32(img.size/int(unsafe.Sizeof(i))); i++ {
   678  					if err := binary.Write(buf, binary.LittleEndian, i+0x7b3184b5); err != nil {
   679  						return err
   680  					}
   681  				}
   682  				return nil
   683  			},
   684  		})
   685  	}
   686  }
   687  
   688  func printResults(images []*Image, shutdown chan struct{}, keepImage, verbose bool) {
   689  	good, failed := 0, 0
   690  	hashes := make(map[uint32][]*Image)
   691  	for _, img := range images {
   692  		err := <-img.done
   693  		if img.disk != "" && !keepImage {
   694  			os.Remove(img.disk)
   695  		}
   696  		select {
   697  		case <-shutdown:
   698  			err = errShutdown
   699  		default:
   700  		}
   701  		if err == errShutdown {
   702  			continue
   703  		}
   704  		res := "ok"
   705  		if err != nil {
   706  			res = fmt.Sprintf("failed:\n\t%v", err)
   707  		}
   708  		if verbose || err != nil {
   709  			fmt.Printf("%v: %v\n", img, res)
   710  		}
   711  		if err != nil {
   712  			failed++
   713  			continue
   714  		}
   715  		hashes[img.hash] = append(hashes[img.hash], img)
   716  		good++
   717  	}
   718  	fmt.Printf("generated images: %v/%v\n", good, len(images))
   719  	for _, img := range images {
   720  		group := hashes[img.hash]
   721  		if len(group) <= 1 {
   722  			continue
   723  		}
   724  		delete(hashes, img.hash)
   725  		fmt.Printf("equal images:\n")
   726  		for _, img := range group {
   727  			fmt.Printf("\tmkfs.%v %v\n", img.fs.Name, img.flags)
   728  		}
   729  	}
   730  	if failed != 0 {
   731  		os.Exit(1)
   732  	}
   733  }
   734  
   735  func generateImages(target *prog.Target, flagFS string, list bool) ([]*Image, error) {
   736  	var images []*Image
   737  	for _, fs := range fileSystems {
   738  		if flagFS != "" && !strings.Contains(","+flagFS+",", ","+fs.Name+",") {
   739  			continue
   740  		}
   741  		index := 0
   742  		enumerateFlags(target, &images, &index, fs, fs.MkfsFlags, 0)
   743  		if list {
   744  			fmt.Printf("%v [%v images]\n", fs.Name, index)
   745  			continue
   746  		}
   747  		files, err := filepath.Glob(filepath.Join("sys", targets.Linux, "test", fs.filePrefix()+"_*"))
   748  		if err != nil {
   749  			return nil, fmt.Errorf("error reading output dir: %w", err)
   750  		}
   751  		for _, file := range files {
   752  			if err := os.Remove(file); err != nil {
   753  				return nil, fmt.Errorf("error removing output file: %w", err)
   754  			}
   755  		}
   756  	}
   757  	return images, nil
   758  }
   759  
   760  func enumerateFlags(target *prog.Target, images *[]*Image, index *int, fs FileSystem, flags []string, flagsIndex int) {
   761  	if flagsIndex == len(fs.MkfsFlagCombinations) {
   762  		*images = append(*images, &Image{
   763  			target: target,
   764  			fs:     fs,
   765  			flags:  append([]string{}, flags...),
   766  			index:  *index,
   767  			done:   make(chan error, 1),
   768  		})
   769  		*index++
   770  		return
   771  	}
   772  	for _, flag := range fs.MkfsFlagCombinations[flagsIndex] {
   773  		flags1 := flags
   774  		for _, f := range strings.Split(flag, " ") {
   775  			if f != "" {
   776  				flags1 = append(flags1, f)
   777  			}
   778  		}
   779  		enumerateFlags(target, images, index, fs, flags1, flagsIndex+1)
   780  	}
   781  }
   782  
   783  func (img *Image) generate() error {
   784  	var err error
   785  	for img.size = img.fs.MinSize; img.size <= 128<<20; img.size *= 2 {
   786  		if err = img.generateSize(); err == nil {
   787  			return nil
   788  		}
   789  	}
   790  	return err
   791  }
   792  
   793  func (img *Image) generateSize() error {
   794  	outFile := filepath.Join("sys", targets.Linux, "test",
   795  		fmt.Sprintf("%v_%v", img.fs.filePrefix(), img.index))
   796  	img.disk = outFile + ".img"
   797  	f, err := os.Create(img.disk)
   798  	if err != nil {
   799  		return err
   800  	}
   801  	f.Close()
   802  	if err := os.Truncate(img.disk, int64(img.size)); err != nil {
   803  		return err
   804  	}
   805  	if img.fs.Mkfs == nil {
   806  		if _, err := runCmd("mkfs."+img.fs.Name, append(img.flags, img.disk)...); err != nil {
   807  			return err
   808  		}
   809  	} else {
   810  		if err := img.fs.Mkfs(img); err != nil {
   811  			return err
   812  		}
   813  	}
   814  	if !img.fs.ReadOnly {
   815  		// This does not work with runCmd -- sudo does not show password prompt on console.
   816  		cmd := exec.Command("sudo", os.Args[0], "-populate", img.disk, "-fs", img.fs.Name)
   817  		if out, err := cmd.CombinedOutput(); err != nil {
   818  			return fmt.Errorf("image population failed: %w\n%s", err, out)
   819  		}
   820  	}
   821  	data, err := os.ReadFile(img.disk)
   822  	if err != nil {
   823  		return err
   824  	}
   825  	img.hash = crc32.ChecksumIEEE(data)
   826  
   827  	// Write out image *with* change of directory.
   828  	out, err := writeImage(img, data)
   829  	if err != nil {
   830  		return fmt.Errorf("failed to write image: %w", err)
   831  	}
   832  
   833  	p, err := img.target.Deserialize(out, prog.Strict)
   834  	if err != nil {
   835  		return fmt.Errorf("failed to deserialize resulting program: %w", err)
   836  	}
   837  	if _, err := p.SerializeForExec(); err != nil {
   838  		return fmt.Errorf("failed to serialize for execution: %w", err)
   839  	}
   840  
   841  	return osutil.WriteFile(outFile, out)
   842  }
   843  
   844  // Runs under sudo in a subprocess.
   845  func populate(disk, fs string) error {
   846  	output, err := runCmd("losetup", "-f", "--show", "-P", disk)
   847  	if err != nil {
   848  		return err
   849  	}
   850  	loop := strings.TrimSpace(string(output))
   851  	defer runCmd("losetup", "-d", loop)
   852  
   853  	dir, err := os.MkdirTemp("", "syz-imagegen")
   854  	if err != nil {
   855  		return err
   856  	}
   857  	defer os.RemoveAll(dir)
   858  	if _, err := runCmd("mount", "-t", fs, loop, dir); err != nil {
   859  		return fmt.Errorf("%w\n%s", err, output)
   860  	}
   861  	defer runCmd("umount", dir)
   862  	return populateDir(dir)
   863  }
   864  
   865  func populateDir(dir string) error {
   866  	zeros := func(size int) []byte {
   867  		return make([]byte, size)
   868  	}
   869  	nonzeros := func(size int) []byte {
   870  		const fill = "syzkaller"
   871  		return bytes.Repeat([]byte(fill), size/len(fill)+1)[:size]
   872  	}
   873  	if err := os.Mkdir(filepath.Join(dir, "file0"), 0777); err != nil {
   874  		return err
   875  	}
   876  	if err := os.WriteFile(filepath.Join(dir, "file0", "file0"), nonzeros(1050), 0777); err != nil {
   877  		return err
   878  	}
   879  	os.Symlink(filepath.Join(dir, "file0", "file0"), filepath.Join(dir, "file0", "file1"))
   880  	if err := os.WriteFile(filepath.Join(dir, "file1"), nonzeros(10), 0777); err != nil {
   881  		return err
   882  	}
   883  	// Note: some errors are not checked because some file systems don't have support for links/attrs.
   884  	// TODO: does it make sense to create other attribute types (system./trusted./security./btrfs.)?
   885  	syscall.Setxattr(filepath.Join(dir, "file1"), "user.xattr1", []byte("xattr1"), 0)
   886  	syscall.Setxattr(filepath.Join(dir, "file1"), "user.xattr2", []byte("xattr2"), 0)
   887  	if err := os.WriteFile(filepath.Join(dir, "file2"), zeros(9000), 0777); err != nil {
   888  		return err
   889  	}
   890  	os.Link(filepath.Join(dir, "file2"), filepath.Join(dir, "file3"))
   891  	// f2fs considers .cold extension specially.
   892  	if err := os.WriteFile(filepath.Join(dir, "file.cold"), nonzeros(100), 0777); err != nil {
   893  		return err
   894  	}
   895  	return nil
   896  }
   897  
   898  func runCmd(cmd string, args ...string) ([]byte, error) {
   899  	return osutil.RunCmd(10*time.Minute, "", cmd, args...)
   900  }
   901  
   902  func writeImage(img *Image, data []byte) ([]byte, error) {
   903  	buf := new(bytes.Buffer)
   904  	fmt.Fprintf(buf, "# Code generated by tools/syz-imagegen. DO NOT EDIT.\n")
   905  	fmt.Fprintf(buf, "# requires: manual\n\n")
   906  	fmt.Fprintf(buf, "# %v\n\n", img)
   907  	compressedData := image.Compress(data)
   908  	b64Data := image.EncodeB64(compressedData)
   909  	if img.fs.Name == parttable {
   910  		fmt.Fprintf(buf, `%s(AUTO, &AUTO="$`, syzReadPartTable)
   911  	} else {
   912  		fmt.Fprintf(buf, `%s$%v(&AUTO='%v\x00', &AUTO='./file0\x00', 0x0, &AUTO, 0x1, AUTO, &AUTO="$`,
   913  			syzMountImage, img.fs.Name, img.fs.Name)
   914  	}
   915  	buf.Write(b64Data)
   916  	fmt.Fprintf(buf, "\")\n")
   917  
   918  	return buf.Bytes(), nil
   919  }