blob: 2c28a2f2cef71f44728daf02bb386586e94c2c34 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
#
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
A helper script to generate sparse files that contain some written sectors. These raw disk images
will be converted to qcow images by qemu-img for testing.
"""
import optparse
import re
import sys
# This largely matches the number formats accepted by qemu-img.
def to_multiplier(c):
multipler = 1
if not c:
return 1
multipler *= 1024
if c == "k" or c == "K":
return multipler
multipler *= 1024
if c == "M":
return multipler
multipler *= 1024
if c == "G":
return multipler
multipler *= 1024
if c == "T":
return multipler
multipler *= 1024
if c == "P":
return multipler
multipler *= 1024
if c == "E":
return multipler
raise ValueError(f'Unsupported size suffix "{c}"')
# Takes a size string (ex: 1k) and converts it into a integer (ex: 1024).
def resolve_size(size):
m = re.match("([0-9]+)([kKMGTPE]?)", size)
if not m:
return None
return int(m[1]) * to_multiplier(m[2])
def gen_image(filename, file_size, writes):
file = open(filename, "wb")
# Truncate to the full size
file.truncate(file_size)
for write in writes:
(offset, size, value) = write
if offset + size > file_size:
raise ValueError("Out of Bounds")
file.seek(offset)
data = bytes([value] * size)
file.write(data)
def main():
parser = optparse.OptionParser()
parser.add_option(
"-f", "--file", dest="filename", help="name of the output file"
)
parser.add_option(
"-s",
"--size",
dest="size",
help="The size of the file to create. This can be in bytes or it can accept suffixes for "
'Kib (k or K), Mib (M), GiB (G), TiB (T), EiB (E), or PiB (P). Ex: "--size=256M" would '
"create a 256 MiB file.",
)
parser.add_option(
"-w",
"--write",
dest="write",
action="append",
help="Write a region of the sparse file. This takes an argument of the form "
'"<offset>+<length>=<byte>" where "offset" and "length" are byte values and can take '
'the same suffixes as the --size argument. The "byte" value must be 0 <= byte < 256 '
'and can be in hex or decimal format. For example --write="1M+2k=3" would populate a 2 KiB '
"region in the file, starting 1 MiB into the file with all bytes set to 3.",
)
options, _ = parser.parse_args()
if not options.filename:
parser.error('Missing required "--filename" option')
if not options.size:
parser.error('Missing required "--size" option')
size = resolve_size(options.size)
if size is None:
parser.error('Invalid "--size" option')
writes = []
if options.write is not None:
for spec in options.write:
m = re.match("(\w+)\+(\w+)=(0x[0-9a-fA-F]+|\w+)", spec)
if not m:
raise ValueError("invalid option!")
break
offset = resolve_size(m[1])
length = resolve_size(m[2])
pattern = int(m[3], 0)
if pattern > 255:
raise ValueError("write value must be a single byte")
writes.append((offset, length, pattern))
gen_image(options.filename, size, writes)
if __name__ == "__main__":
sys.exit(main())