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// imagegen generates syz_mount_image/syz_read_part_table calls from disk images.
5package main
6
7import (
8	"bytes"
9	"encoding/hex"
10	"flag"
11	"fmt"
12	"io/ioutil"
13	"os"
14)
15
16type Segment struct {
17	offset int
18	data   []byte
19}
20
21func main() {
22	flagV := flag.Bool("v", false, "verbose output")
23	flagImage := flag.String("image", "", "image file")
24	flagSkip := flag.Int("skip", 32, "min zero bytes to skip")
25	flagAlign := flag.Int("align", 32, "non-zero block alignment")
26	flagFS := flag.String("fs", "", "filesystem")
27	flag.Parse()
28	data, err := ioutil.ReadFile(*flagImage)
29	if err != nil {
30		fmt.Fprintf(os.Stderr, "failed to read input file: %v\n", err)
31		os.Exit(1)
32	}
33	data0 := data
34	zeros := make([]byte, *flagAlign+*flagSkip)
35	var segs []Segment
36	offset := 0
37	for len(data) != 0 {
38		pos := bytes.Index(data, zeros)
39		if pos == -1 {
40			segs = append(segs, Segment{offset, data})
41			break
42		}
43		pos = (pos + *flagAlign - 1) & ^(*flagAlign - 1)
44		if pos != 0 {
45			segs = append(segs, Segment{offset, data[:pos]})
46		}
47		for pos < len(data) && data[pos] == 0 {
48			pos++
49		}
50		pos = pos & ^(*flagAlign - 1)
51		offset += pos
52		data = data[pos:]
53	}
54	totalData := 0
55	for _, seg := range segs {
56		totalData += len(seg.data)
57	}
58	fmt.Fprintf(os.Stderr, "image size: %v, segments: %v, data: %v\n",
59		len(data0), len(segs), totalData)
60	if *flagV {
61		for i, seg := range segs {
62			next := len(data0)
63			if i != len(segs)-1 {
64				next = segs[i+1].offset
65			}
66			skip := next - seg.offset - len(seg.data)
67			fmt.Fprintf(os.Stderr, "segment: %8v-%8v [%8v] -%8v\n",
68				seg.offset, seg.offset+len(seg.data), len(seg.data), skip)
69		}
70	}
71	restored := make([]byte, len(data0))
72	for _, seg := range segs {
73		copy(restored[seg.offset:], seg.data)
74	}
75	if !bytes.Equal(data0, restored) {
76		fmt.Fprintf(os.Stderr, "restored data differs!\n")
77		os.Exit(1)
78	}
79	if *flagFS == "part" {
80		fmt.Printf(`syz_read_part_table(0x%x, 0x%x, &(0x7f0000000200)=[`,
81			len(data0), len(segs))
82		addr := 0x7f0000010000
83		for i, seg := range segs {
84			if i != 0 {
85				fmt.Printf(", ")
86			}
87			fmt.Printf(`{&(0x%x)="%v", 0x%x, 0x%x}`,
88				addr, hex.EncodeToString(seg.data), len(seg.data), seg.offset)
89			addr = (addr + len(seg.data) + 0xff) & ^0xff
90		}
91		fmt.Printf("])\n")
92	} else {
93		syscallSuffix := *flagFS
94		if syscallSuffix == "ext2" || syscallSuffix == "ext3" {
95			syscallSuffix = "ext4"
96		}
97		fmt.Printf(`syz_mount_image$%v(&(0x7f0000000000)='%v\x00', &(0x7f0000000100)='./file0\x00',`+
98			` 0x%x, 0x%x, &(0x7f0000000200)=[`,
99			syscallSuffix, *flagFS, len(data0), len(segs))
100		addr := 0x7f0000010000
101		for i, seg := range segs {
102			if i != 0 {
103				fmt.Printf(", ")
104			}
105			fmt.Printf(`{&(0x%x)="%v", 0x%x, 0x%x}`,
106				addr, hex.EncodeToString(seg.data), len(seg.data), seg.offset)
107			addr = (addr + len(seg.data) + 0xff) & ^0xff
108		}
109		fmt.Printf("], 0x0, &(0x%x))\n", addr)
110	}
111}
112