mirror of
git://git.openembedded.org/meta-openembedded
synced 2026-09-27 04:33:29 +00:00
image_types_verity.bbclass: add FEC support
dm-verity can use Reed-Solomon forward error correction (FEC) data to transparently recover a limited number of corrupted blocks instead of only detecting them. This can be useful for mitigating error-prone storage. We generate this FEC data as an option, controlled by VERITY_FEC (default off, same behavior as before) and VERITY_FEC_ROOTS (number of Reed-Solomon roots, default 2). The FEC data is appended to the hash tree by default. If VERITY_IMAGE_FECDEV_SUFFIX is set, a separate file with the given suffix will be used. When enabled, the parameter file gains additional variables necessary for correct DM table generation. Recipes that do not set VERITY_FEC = "1" behave the same as before. Signed-off-by: Andreas Zdziarstek <andreas.zdziarstek@gmail.com> Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
This commit is contained in:
parent
19a6074451
commit
436ddf7860
@ -36,6 +36,60 @@
|
|||||||
# dd if=/dev/random bs=1k count=1 | sha256sum
|
# dd if=/dev/random bs=1k count=1 | sha256sum
|
||||||
#
|
#
|
||||||
# and assign it to the parameter VERITY_SALT.
|
# and assign it to the parameter VERITY_SALT.
|
||||||
|
#
|
||||||
|
# Forward Error Correction (FEC) can optionally be generated by setting
|
||||||
|
# VERITY_FEC to "1". dm-verity then transparently recovers a limited number of
|
||||||
|
# corrupted blocks instead of only detecting them.
|
||||||
|
#
|
||||||
|
# The number of Reed-Solomon roots is set via VERITY_FEC_ROOTS (default 2, max
|
||||||
|
# 24). Higher number of roots means higher error resilience but also more
|
||||||
|
# overhead in terms of space.
|
||||||
|
#
|
||||||
|
# The FEC data is either appended after the hash tree or put in its own file,
|
||||||
|
# using VERITY_IMAGE_FECDEV_SUFFIX.
|
||||||
|
#
|
||||||
|
# When FEC is enabled, additional values are appended to the parameter file:
|
||||||
|
#
|
||||||
|
# VERITY_FEC_RS_ROOTS - number of Reed-Solomon roots
|
||||||
|
# VERITY_FEC_BLOCKS - number of parity blocks generated on the FEC device.
|
||||||
|
# This is the FEC overhead and is NOT the kernel table's
|
||||||
|
# "fec_blocks" argument (see the example below).
|
||||||
|
# VERITY_FEC_START - offset (in VERITY_DATA_BLOCK_SIZE blocks) of the FEC
|
||||||
|
# data on the FEC device.
|
||||||
|
#
|
||||||
|
# extending the dmsetup example above (with <fec_dev> as the FEC blockdevice) to
|
||||||
|
#
|
||||||
|
# . <IMAGE_LINK_NAME>.verity-params
|
||||||
|
# dmsetup create <dm_dev_name> --readonly --table "0 $VERITY_DATA_SECTORS \
|
||||||
|
# verity 1 <dev> <hash_dev> \
|
||||||
|
# $VERITY_DATA_BLOCK_SIZE $VERITY_HASH_BLOCK_SIZE \
|
||||||
|
# $VERITY_DATA_BLOCKS $VERITY_DATA_BLOCKS \
|
||||||
|
# $VERITY_HASH_ALGORITHM $VERITY_ROOT_HASH $VERITY_SALT \
|
||||||
|
# 9 ignore_zero_blocks use_fec_from_device <fec_dev> \
|
||||||
|
# fec_roots $VERITY_FEC_RS_ROOTS \
|
||||||
|
# fec_blocks $((VERITY_DATA_BLOCKS + VERITY_HASH_BLOCKS)) \
|
||||||
|
# fec_start $VERITY_FEC_START"
|
||||||
|
#
|
||||||
|
# Note that the kernel's "fec_blocks" is the number of blocks covered by FEC,
|
||||||
|
# i.e. the data and hash blocks together (VERITY_DATA_BLOCKS +
|
||||||
|
# VERITY_HASH_BLOCKS), and differs from VERITY_FEC_BLOCKS, which counts the
|
||||||
|
# generated parity blocks.
|
||||||
|
#
|
||||||
|
# The same device as above can be configured with veritysetup, using
|
||||||
|
#
|
||||||
|
# . <IMAGE_LINK_NAME>.verity-params
|
||||||
|
# veritysetup open --no-superblock <dev> <dm_dev_name> <dev> $VERITY_ROOT_HASH \
|
||||||
|
# --data-block-size=$VERITY_DATA_BLOCK_SIZE \
|
||||||
|
# --hash-block-size=$VERITY_HASH_BLOCK_SIZE \
|
||||||
|
# --data-blocks=$VERITY_DATA_BLOCKS \
|
||||||
|
# --hash-offset=$((VERITY_DATA_BLOCKS * VERITY_DATA_BLOCK_SIZE)) \
|
||||||
|
# --salt=$VERITY_SALT \
|
||||||
|
# --fec-device=<dev> \
|
||||||
|
# --fec-offset=$((VERITY_FEC_START * VERITY_DATA_BLOCK_SIZE)) \
|
||||||
|
# --fec-roots=$VERITY_FEC_RS_ROOTS
|
||||||
|
#
|
||||||
|
# NOTE: To be able to use dm-verity with FEC, your kernel must have
|
||||||
|
# CONFIG_DM_VERITY_FEC enabled.
|
||||||
|
|
||||||
inherit image-artifact-names
|
inherit image-artifact-names
|
||||||
|
|
||||||
@ -48,6 +102,9 @@ VERITY_IMAGE_FSTYPE ?= "ext4"
|
|||||||
VERITY_IMAGE_SUFFIX ?= ".verity"
|
VERITY_IMAGE_SUFFIX ?= ".verity"
|
||||||
VERITY_IMAGE_HASHDEV_SUFFIX ?= "${VERITY_IMAGE_SUFFIX}"
|
VERITY_IMAGE_HASHDEV_SUFFIX ?= "${VERITY_IMAGE_SUFFIX}"
|
||||||
VERITY_INPUT_IMAGE ?= "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.${VERITY_IMAGE_FSTYPE}"
|
VERITY_INPUT_IMAGE ?= "${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.${VERITY_IMAGE_FSTYPE}"
|
||||||
|
VERITY_FEC ?= ""
|
||||||
|
VERITY_FEC_ROOTS ?= "2"
|
||||||
|
VERITY_IMAGE_FECDEV_SUFFIX ?= "${VERITY_IMAGE_HASHDEV_SUFFIX}"
|
||||||
|
|
||||||
IMAGE_TYPEDEP:verity = "${VERITY_IMAGE_FSTYPE}"
|
IMAGE_TYPEDEP:verity = "${VERITY_IMAGE_FSTYPE}"
|
||||||
IMAGE_TYPES_MASKED += "verity"
|
IMAGE_TYPES_MASKED += "verity"
|
||||||
@ -75,6 +132,10 @@ python do_image_verity () {
|
|||||||
verity_image_hashdev_suffix = d.getVar('VERITY_IMAGE_HASHDEV_SUFFIX')
|
verity_image_hashdev_suffix = d.getVar('VERITY_IMAGE_HASHDEV_SUFFIX')
|
||||||
verity_hashdev = '{}{}'.format(image, verity_image_hashdev_suffix)
|
verity_hashdev = '{}{}'.format(image, verity_image_hashdev_suffix)
|
||||||
|
|
||||||
|
fec = bb.utils.to_boolean(d.getVar('VERITY_FEC'))
|
||||||
|
verity_image_fecdev_suffix = d.getVar('VERITY_IMAGE_FECDEV_SUFFIX')
|
||||||
|
verity_fecdev = '{}{}'.format(image, verity_image_fecdev_suffix)
|
||||||
|
|
||||||
# For better readability the parameter VERITY_BLOCK_SIZE is specified in
|
# For better readability the parameter VERITY_BLOCK_SIZE is specified in
|
||||||
# bytes. It must be a multiple of the logical sector size which is 512 bytes
|
# bytes. It must be a multiple of the logical sector size which is 512 bytes
|
||||||
# in Linux. Make sure that this is the case as otherwise the resulting
|
# in Linux. Make sure that this is the case as otherwise the resulting
|
||||||
@ -87,6 +148,11 @@ python do_image_verity () {
|
|||||||
if salt == d.getVar('CLASS_VERITY_SALT'):
|
if salt == d.getVar('CLASS_VERITY_SALT'):
|
||||||
bb.warn("Please overwrite VERITY_SALT with an image specific one!")
|
bb.warn("Please overwrite VERITY_SALT with an image specific one!")
|
||||||
|
|
||||||
|
if fec:
|
||||||
|
fec_roots = int(d.getVar('VERITY_FEC_ROOTS'))
|
||||||
|
if not 2 <= fec_roots <= 24:
|
||||||
|
bb.fatal("VERITY_FEC_ROOTS must be between 2 and 24 (got %d)!" % fec_roots)
|
||||||
|
|
||||||
shutil.copyfile(image, verity)
|
shutil.copyfile(image, verity)
|
||||||
|
|
||||||
data_size_blocks, data_size_rest = divmod(os.stat(verity).st_size, block_size)
|
data_size_blocks, data_size_rest = divmod(os.stat(verity).st_size, block_size)
|
||||||
@ -125,6 +191,26 @@ python do_image_verity () {
|
|||||||
verityfile.seek(0, io.SEEK_END)
|
verityfile.seek(0, io.SEEK_END)
|
||||||
verityfile.write(b'\x00' * (block_size - data_size_rest))
|
verityfile.write(b'\x00' * (block_size - data_size_rest))
|
||||||
|
|
||||||
|
# Optionally generate Reed-Solomon FEC data. If the FEC data shares a file
|
||||||
|
# with the hash tree, it must start past it. Do a first veritysetup pass to
|
||||||
|
# get the size of the hash tree and calculate the offset. With a dedicated
|
||||||
|
# FEC file the FEC data starts at offset 0 and a single pass suffices.
|
||||||
|
fec_offset = 0
|
||||||
|
if fec:
|
||||||
|
if verity_fecdev == verity_hashdev:
|
||||||
|
try:
|
||||||
|
subprocess.check_output(veritysetup_command)
|
||||||
|
except subprocess.CalledProcessError as err:
|
||||||
|
bb.fatal('%s returned with %s (%s)' % (err.cmd, err.returncode, err.output))
|
||||||
|
fec_offset = os.stat(verity_hashdev).st_size
|
||||||
|
if fec_offset % block_size != 0:
|
||||||
|
bb.fatal("hash tree end (%d) is not a multiple of the block size" % fec_offset)
|
||||||
|
veritysetup_command.append('--fec-offset={}'.format(fec_offset))
|
||||||
|
veritysetup_command += [
|
||||||
|
'--fec-device={}'.format(verity_fecdev),
|
||||||
|
'--fec-roots={}'.format(fec_roots),
|
||||||
|
]
|
||||||
|
|
||||||
# Create verity image
|
# Create verity image
|
||||||
try:
|
try:
|
||||||
output = subprocess.check_output(veritysetup_command)
|
output = subprocess.check_output(veritysetup_command)
|
||||||
@ -151,6 +237,13 @@ python do_image_verity () {
|
|||||||
|
|
||||||
params.append('VERITY_DATA_SECTORS={}'.format(data_size//512))
|
params.append('VERITY_DATA_SECTORS={}'.format(data_size//512))
|
||||||
|
|
||||||
|
if fec:
|
||||||
|
# Where the FEC data starts on its device, in blocks. It's not part
|
||||||
|
# of veritysetup's output, so emit it for `dmsetup create ...`.
|
||||||
|
# A byte offset, if needed (e.g. for `veritysetup open ...`), is
|
||||||
|
# VERITY_FEC_START * VERITY_DATA_BLOCK_SIZE.
|
||||||
|
params.append('VERITY_FEC_START={}'.format(fec_offset//block_size))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(image + '.verity-params', 'w') as f:
|
with open(image + '.verity-params', 'w') as f:
|
||||||
f.write('\n'.join(params))
|
f.write('\n'.join(params))
|
||||||
@ -161,6 +254,8 @@ python do_image_verity () {
|
|||||||
suffix_list = [ verity_image_suffix, '.verity-info', '.verity-params' ]
|
suffix_list = [ verity_image_suffix, '.verity-info', '.verity-params' ]
|
||||||
if verity != verity_hashdev:
|
if verity != verity_hashdev:
|
||||||
suffix_list.append(verity_image_hashdev_suffix)
|
suffix_list.append(verity_image_hashdev_suffix)
|
||||||
|
if fec and verity_fecdev not in (verity, verity_hashdev):
|
||||||
|
suffix_list.append(verity_image_fecdev_suffix)
|
||||||
|
|
||||||
for suffix in suffix_list:
|
for suffix in suffix_list:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user