Merge "init: do_mounts: Add a dummy definition for dm_table_put"
This commit is contained in:
commit
44057c87c7
10 changed files with 1119 additions and 24 deletions
|
@ -517,4 +517,20 @@ config DM_LOG_WRITES
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config DM_ANDROID_VERITY
|
||||
bool "Android verity target support"
|
||||
depends on DM_VERITY
|
||||
depends on X509_CERTIFICATE_PARSER
|
||||
depends on SYSTEM_TRUSTED_KEYRING
|
||||
depends on PUBLIC_KEY_ALGO_RSA
|
||||
depends on KEYS
|
||||
depends on ASYMMETRIC_KEY_TYPE
|
||||
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
|
||||
---help---
|
||||
This device-mapper target is virtually a VERITY target. This
|
||||
target is setup by reading the metadata contents piggybacked
|
||||
to the actual data blocks in the block device. The signature
|
||||
of the metadata contents are verified against the key included
|
||||
in the system keyring. Upon success, the underlying verity
|
||||
target is setup.
|
||||
endif # MD
|
||||
|
|
|
@ -69,3 +69,7 @@ endif
|
|||
ifeq ($(CONFIG_DM_VERITY_FEC),y)
|
||||
dm-verity-objs += dm-verity-fec.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DM_ANDROID_VERITY),y)
|
||||
dm-verity-objs += dm-android-verity.o
|
||||
endif
|
||||
|
|
925
drivers/md/dm-android-verity.c
Normal file
925
drivers/md/dm-android-verity.c
Normal file
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/device-mapper.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
|
||||
#include "dm-verity.h"
|
||||
#include "dm-android-verity.h"
|
||||
|
||||
static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH];
|
||||
static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH];
|
||||
static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH];
|
||||
static char buildvariant[BUILD_VARIANT];
|
||||
|
||||
static bool target_added;
|
||||
static bool verity_enabled = true;
|
||||
struct dentry *debug_dir;
|
||||
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
|
||||
|
||||
static struct target_type android_verity_target = {
|
||||
.name = "android-verity",
|
||||
.version = {1, 0, 0},
|
||||
.module = THIS_MODULE,
|
||||
.ctr = android_verity_ctr,
|
||||
.dtr = verity_dtr,
|
||||
.map = verity_map,
|
||||
.status = verity_status,
|
||||
.prepare_ioctl = verity_prepare_ioctl,
|
||||
.iterate_devices = verity_iterate_devices,
|
||||
.io_hints = verity_io_hints,
|
||||
};
|
||||
|
||||
static int __init verified_boot_state_param(char *line)
|
||||
{
|
||||
strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("androidboot.verifiedbootstate=", verified_boot_state_param);
|
||||
|
||||
static int __init verity_mode_param(char *line)
|
||||
{
|
||||
strlcpy(veritymode, line, sizeof(veritymode));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("androidboot.veritymode=", verity_mode_param);
|
||||
|
||||
static int __init verity_keyid_param(char *line)
|
||||
{
|
||||
strlcpy(veritykeyid, line, sizeof(veritykeyid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("veritykeyid=", verity_keyid_param);
|
||||
|
||||
static int __init verity_buildvariant(char *line)
|
||||
{
|
||||
strlcpy(buildvariant, line, sizeof(buildvariant));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("buildvariant=", verity_buildvariant);
|
||||
|
||||
static inline bool default_verity_key_id(void)
|
||||
{
|
||||
return veritykeyid[0] != '\0';
|
||||
}
|
||||
|
||||
static inline bool is_eng(void)
|
||||
{
|
||||
static const char typeeng[] = "eng";
|
||||
|
||||
return !strncmp(buildvariant, typeeng, sizeof(typeeng));
|
||||
}
|
||||
|
||||
static inline bool is_userdebug(void)
|
||||
{
|
||||
static const char typeuserdebug[] = "userdebug";
|
||||
|
||||
return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug));
|
||||
}
|
||||
|
||||
|
||||
static int table_extract_mpi_array(struct public_key_signature *pks,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
MPI mpi = mpi_read_raw_data(data, len);
|
||||
|
||||
if (!mpi) {
|
||||
DMERR("Error while allocating mpi array");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pks->mpi[0] = mpi;
|
||||
pks->nr_mpi = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct public_key_signature *table_make_digest(
|
||||
enum hash_algo hash,
|
||||
const void *table,
|
||||
unsigned long table_len)
|
||||
{
|
||||
struct public_key_signature *pks = NULL;
|
||||
struct crypto_shash *tfm;
|
||||
struct shash_desc *desc;
|
||||
size_t digest_size, desc_size;
|
||||
int ret;
|
||||
|
||||
/* Allocate the hashing algorithm we're going to need and find out how
|
||||
* big the hash operational data will be.
|
||||
*/
|
||||
tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0);
|
||||
if (IS_ERR(tfm))
|
||||
return ERR_CAST(tfm);
|
||||
|
||||
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
||||
digest_size = crypto_shash_digestsize(tfm);
|
||||
|
||||
/* We allocate the hash operational data storage on the end of out
|
||||
* context data and the digest output buffer on the end of that.
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL);
|
||||
if (!pks)
|
||||
goto error;
|
||||
|
||||
pks->pkey_hash_algo = hash;
|
||||
pks->digest = (u8 *)pks + sizeof(*pks) + desc_size;
|
||||
pks->digest_size = digest_size;
|
||||
|
||||
desc = (struct shash_desc *)(pks + 1);
|
||||
desc->tfm = tfm;
|
||||
desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
ret = crypto_shash_init(desc);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = crypto_shash_finup(desc, table, table_len, pks->digest);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
crypto_free_shash(tfm);
|
||||
return pks;
|
||||
|
||||
error:
|
||||
kfree(pks);
|
||||
crypto_free_shash(tfm);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int read_block_dev(struct bio_read *payload, struct block_device *bdev,
|
||||
sector_t offset, int length)
|
||||
{
|
||||
struct bio *bio;
|
||||
int err = 0, i;
|
||||
|
||||
payload->number_of_pages = DIV_ROUND_UP(length, PAGE_SIZE);
|
||||
|
||||
bio = bio_alloc(GFP_KERNEL, payload->number_of_pages);
|
||||
if (!bio) {
|
||||
DMERR("Error while allocating bio");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bio->bi_bdev = bdev;
|
||||
bio->bi_iter.bi_sector = offset;
|
||||
|
||||
payload->page_io = kzalloc(sizeof(struct page *) *
|
||||
payload->number_of_pages, GFP_KERNEL);
|
||||
if (!payload->page_io) {
|
||||
DMERR("page_io array alloc failed");
|
||||
err = -ENOMEM;
|
||||
goto free_bio;
|
||||
}
|
||||
|
||||
for (i = 0; i < payload->number_of_pages; i++) {
|
||||
payload->page_io[i] = alloc_page(GFP_KERNEL);
|
||||
if (!payload->page_io[i]) {
|
||||
DMERR("alloc_page failed");
|
||||
err = -ENOMEM;
|
||||
goto free_pages;
|
||||
}
|
||||
if (!bio_add_page(bio, payload->page_io[i], PAGE_SIZE, 0)) {
|
||||
DMERR("bio_add_page error");
|
||||
err = -EIO;
|
||||
goto free_pages;
|
||||
}
|
||||
}
|
||||
|
||||
if (!submit_bio_wait(READ, bio))
|
||||
/* success */
|
||||
goto free_bio;
|
||||
DMERR("bio read failed");
|
||||
err = -EIO;
|
||||
|
||||
free_pages:
|
||||
for (i = 0; i < payload->number_of_pages; i++)
|
||||
if (payload->page_io[i])
|
||||
__free_page(payload->page_io[i]);
|
||||
kfree(payload->page_io);
|
||||
free_bio:
|
||||
bio_put(bio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline u64 fec_div_round_up(u64 x, u64 y)
|
||||
{
|
||||
u64 remainder;
|
||||
|
||||
return div64_u64_rem(x, y, &remainder) +
|
||||
(remainder > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline void populate_fec_metadata(struct fec_header *header,
|
||||
struct fec_ecc_metadata *ecc)
|
||||
{
|
||||
ecc->blocks = fec_div_round_up(le64_to_cpu(header->inp_size),
|
||||
FEC_BLOCK_SIZE);
|
||||
ecc->roots = le32_to_cpu(header->roots);
|
||||
ecc->start = le64_to_cpu(header->inp_size);
|
||||
}
|
||||
|
||||
static inline int validate_fec_header(struct fec_header *header, u64 offset)
|
||||
{
|
||||
/* move offset to make the sanity check work for backup header
|
||||
* as well. */
|
||||
offset -= offset % FEC_BLOCK_SIZE;
|
||||
if (le32_to_cpu(header->magic) != FEC_MAGIC ||
|
||||
le32_to_cpu(header->version) != FEC_VERSION ||
|
||||
le32_to_cpu(header->size) != sizeof(struct fec_header) ||
|
||||
le32_to_cpu(header->roots) == 0 ||
|
||||
le32_to_cpu(header->roots) >= FEC_RSM)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extract_fec_header(dev_t dev, struct fec_header *fec,
|
||||
struct fec_ecc_metadata *ecc)
|
||||
{
|
||||
u64 device_size;
|
||||
struct bio_read payload;
|
||||
int i, err = 0;
|
||||
struct block_device *bdev;
|
||||
|
||||
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
|
||||
|
||||
if (IS_ERR_OR_NULL(bdev)) {
|
||||
DMERR("bdev get error");
|
||||
return PTR_ERR(bdev);
|
||||
}
|
||||
|
||||
device_size = i_size_read(bdev->bd_inode);
|
||||
|
||||
/* fec metadata size is a power of 2 and PAGE_SIZE
|
||||
* is a power of 2 as well.
|
||||
*/
|
||||
BUG_ON(FEC_BLOCK_SIZE > PAGE_SIZE);
|
||||
/* 512 byte sector alignment */
|
||||
BUG_ON(((device_size - FEC_BLOCK_SIZE) % (1 << SECTOR_SHIFT)) != 0);
|
||||
|
||||
err = read_block_dev(&payload, bdev, (device_size -
|
||||
FEC_BLOCK_SIZE) / (1 << SECTOR_SHIFT), FEC_BLOCK_SIZE);
|
||||
if (err) {
|
||||
DMERR("Error while reading verity metadata");
|
||||
goto error;
|
||||
}
|
||||
|
||||
BUG_ON(sizeof(struct fec_header) > PAGE_SIZE);
|
||||
memcpy(fec, page_address(payload.page_io[0]),
|
||||
sizeof(*fec));
|
||||
|
||||
ecc->valid = true;
|
||||
if (validate_fec_header(fec, device_size - FEC_BLOCK_SIZE)) {
|
||||
/* Try the backup header */
|
||||
memcpy(fec, page_address(payload.page_io[0]) + FEC_BLOCK_SIZE
|
||||
- sizeof(*fec) ,
|
||||
sizeof(*fec));
|
||||
if (validate_fec_header(fec, device_size -
|
||||
sizeof(struct fec_header)))
|
||||
ecc->valid = false;
|
||||
}
|
||||
|
||||
if (ecc->valid)
|
||||
populate_fec_metadata(fec, ecc);
|
||||
|
||||
for (i = 0; i < payload.number_of_pages; i++)
|
||||
__free_page(payload.page_io[i]);
|
||||
kfree(payload.page_io);
|
||||
|
||||
error:
|
||||
blkdev_put(bdev, FMODE_READ);
|
||||
return err;
|
||||
}
|
||||
static void find_metadata_offset(struct fec_header *fec,
|
||||
struct block_device *bdev, u64 *metadata_offset)
|
||||
{
|
||||
u64 device_size;
|
||||
|
||||
device_size = i_size_read(bdev->bd_inode);
|
||||
|
||||
if (le32_to_cpu(fec->magic) == FEC_MAGIC)
|
||||
*metadata_offset = le64_to_cpu(fec->inp_size) -
|
||||
VERITY_METADATA_SIZE;
|
||||
else
|
||||
*metadata_offset = device_size - VERITY_METADATA_SIZE;
|
||||
}
|
||||
|
||||
static int find_size(dev_t dev, u64 *device_size)
|
||||
{
|
||||
struct block_device *bdev;
|
||||
|
||||
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
|
||||
if (IS_ERR_OR_NULL(bdev)) {
|
||||
DMERR("blkdev_get_by_dev failed");
|
||||
return PTR_ERR(bdev);
|
||||
}
|
||||
|
||||
*device_size = i_size_read(bdev->bd_inode);
|
||||
*device_size >>= SECTOR_SHIFT;
|
||||
|
||||
DMINFO("blkdev size in sectors: %llu", *device_size);
|
||||
blkdev_put(bdev, FMODE_READ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_header(struct android_metadata_header *header)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (is_userdebug() && le32_to_cpu(header->magic_number) ==
|
||||
VERITY_METADATA_MAGIC_DISABLE)
|
||||
return VERITY_STATE_DISABLE;
|
||||
|
||||
if (!(le32_to_cpu(header->magic_number) ==
|
||||
VERITY_METADATA_MAGIC_NUMBER) ||
|
||||
(le32_to_cpu(header->magic_number) ==
|
||||
VERITY_METADATA_MAGIC_DISABLE)) {
|
||||
DMERR("Incorrect magic number");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(header->protocol_version) !=
|
||||
VERITY_METADATA_VERSION) {
|
||||
DMERR("Unsupported version %u",
|
||||
le32_to_cpu(header->protocol_version));
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int extract_metadata(dev_t dev, struct fec_header *fec,
|
||||
struct android_metadata **metadata,
|
||||
bool *verity_enabled)
|
||||
{
|
||||
struct block_device *bdev;
|
||||
struct android_metadata_header *header;
|
||||
int i;
|
||||
u32 table_length, copy_length, offset;
|
||||
u64 metadata_offset;
|
||||
struct bio_read payload;
|
||||
int err = 0;
|
||||
|
||||
bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL);
|
||||
|
||||
if (IS_ERR_OR_NULL(bdev)) {
|
||||
DMERR("blkdev_get_by_dev failed");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
find_metadata_offset(fec, bdev, &metadata_offset);
|
||||
|
||||
/* Verity metadata size is a power of 2 and PAGE_SIZE
|
||||
* is a power of 2 as well.
|
||||
* PAGE_SIZE is also a multiple of 512 bytes.
|
||||
*/
|
||||
if (VERITY_METADATA_SIZE > PAGE_SIZE)
|
||||
BUG_ON(VERITY_METADATA_SIZE % PAGE_SIZE != 0);
|
||||
/* 512 byte sector alignment */
|
||||
BUG_ON(metadata_offset % (1 << SECTOR_SHIFT) != 0);
|
||||
|
||||
err = read_block_dev(&payload, bdev, metadata_offset /
|
||||
(1 << SECTOR_SHIFT), VERITY_METADATA_SIZE);
|
||||
if (err) {
|
||||
DMERR("Error while reading verity metadata");
|
||||
goto blkdev_release;
|
||||
}
|
||||
|
||||
header = kzalloc(sizeof(*header), GFP_KERNEL);
|
||||
if (!header) {
|
||||
DMERR("kzalloc failed for header");
|
||||
err = -ENOMEM;
|
||||
goto free_payload;
|
||||
}
|
||||
|
||||
memcpy(header, page_address(payload.page_io[0]),
|
||||
sizeof(*header));
|
||||
|
||||
DMINFO("bio magic_number:%u protocol_version:%d table_length:%u",
|
||||
le32_to_cpu(header->magic_number),
|
||||
le32_to_cpu(header->protocol_version),
|
||||
le32_to_cpu(header->table_length));
|
||||
|
||||
err = verify_header(header);
|
||||
|
||||
if (err == VERITY_STATE_DISABLE) {
|
||||
DMERR("Mounting root with verity disabled");
|
||||
*verity_enabled = false;
|
||||
/* we would still have to read the metadata to figure out
|
||||
* the data blocks size. Or may be could map the entire
|
||||
* partition similar to mounting the device.
|
||||
*
|
||||
* Reset error as well as the verity_enabled flag is changed.
|
||||
*/
|
||||
err = 0;
|
||||
} else if (err)
|
||||
goto free_header;
|
||||
|
||||
*metadata = kzalloc(sizeof(**metadata), GFP_KERNEL);
|
||||
if (!*metadata) {
|
||||
DMERR("kzalloc for metadata failed");
|
||||
err = -ENOMEM;
|
||||
goto free_header;
|
||||
}
|
||||
|
||||
(*metadata)->header = header;
|
||||
table_length = le32_to_cpu(header->table_length);
|
||||
|
||||
if (table_length == 0 ||
|
||||
table_length > (VERITY_METADATA_SIZE -
|
||||
sizeof(struct android_metadata_header))) {
|
||||
DMERR("table_length too long");
|
||||
err = -EINVAL;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
(*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL);
|
||||
|
||||
if (!(*metadata)->verity_table) {
|
||||
DMERR("kzalloc verity_table failed");
|
||||
err = -ENOMEM;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
if (sizeof(struct android_metadata_header) +
|
||||
table_length <= PAGE_SIZE) {
|
||||
memcpy((*metadata)->verity_table,
|
||||
page_address(payload.page_io[0])
|
||||
+ sizeof(struct android_metadata_header),
|
||||
table_length);
|
||||
} else {
|
||||
copy_length = PAGE_SIZE -
|
||||
sizeof(struct android_metadata_header);
|
||||
memcpy((*metadata)->verity_table,
|
||||
page_address(payload.page_io[0])
|
||||
+ sizeof(struct android_metadata_header),
|
||||
copy_length);
|
||||
table_length -= copy_length;
|
||||
offset = copy_length;
|
||||
i = 1;
|
||||
while (table_length != 0) {
|
||||
if (table_length > PAGE_SIZE) {
|
||||
memcpy((*metadata)->verity_table + offset,
|
||||
page_address(payload.page_io[i]),
|
||||
PAGE_SIZE);
|
||||
offset += PAGE_SIZE;
|
||||
table_length -= PAGE_SIZE;
|
||||
} else {
|
||||
memcpy((*metadata)->verity_table + offset,
|
||||
page_address(payload.page_io[i]),
|
||||
table_length);
|
||||
table_length = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
(*metadata)->verity_table[table_length] = '\0';
|
||||
|
||||
DMINFO("verity_table: %s", (*metadata)->verity_table);
|
||||
goto free_payload;
|
||||
|
||||
free_metadata:
|
||||
kfree(*metadata);
|
||||
free_header:
|
||||
kfree(header);
|
||||
free_payload:
|
||||
for (i = 0; i < payload.number_of_pages; i++)
|
||||
if (payload.page_io[i])
|
||||
__free_page(payload.page_io[i]);
|
||||
kfree(payload.page_io);
|
||||
blkdev_release:
|
||||
blkdev_put(bdev, FMODE_READ);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* helper functions to extract properties from dts */
|
||||
const char *find_dt_value(const char *name)
|
||||
{
|
||||
struct device_node *firmware;
|
||||
const char *value;
|
||||
|
||||
firmware = of_find_node_by_path("/firmware/android");
|
||||
if (!firmware)
|
||||
return NULL;
|
||||
value = of_get_property(firmware, name, NULL);
|
||||
of_node_put(firmware);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int verity_mode(void)
|
||||
{
|
||||
static const char enforcing[] = "enforcing";
|
||||
static const char verified_mode_prop[] = "veritymode";
|
||||
const char *value;
|
||||
|
||||
value = find_dt_value(verified_mode_prop);
|
||||
if (!value)
|
||||
value = veritymode;
|
||||
if (!strncmp(value, enforcing, sizeof(enforcing) - 1))
|
||||
return DM_VERITY_MODE_RESTART;
|
||||
|
||||
return DM_VERITY_MODE_EIO;
|
||||
}
|
||||
|
||||
static int verify_verity_signature(char *key_id,
|
||||
struct android_metadata *metadata)
|
||||
{
|
||||
key_ref_t key_ref;
|
||||
struct key *key;
|
||||
struct public_key_signature *pks = NULL;
|
||||
int retval = -EINVAL;
|
||||
|
||||
key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1),
|
||||
&key_type_asymmetric, key_id);
|
||||
|
||||
if (IS_ERR(key_ref)) {
|
||||
DMERR("keyring: key not found");
|
||||
return -ENOKEY;
|
||||
}
|
||||
|
||||
key = key_ref_to_ptr(key_ref);
|
||||
|
||||
pks = table_make_digest(HASH_ALGO_SHA256,
|
||||
(const void *)metadata->verity_table,
|
||||
le32_to_cpu(metadata->header->table_length));
|
||||
|
||||
if (IS_ERR(pks)) {
|
||||
DMERR("hashing failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = table_extract_mpi_array(pks, &metadata->header->signature[0],
|
||||
RSANUMBYTES);
|
||||
if (retval < 0) {
|
||||
DMERR("Error extracting mpi %d", retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
retval = verify_signature(key, pks);
|
||||
mpi_free(pks->rsa.s);
|
||||
error:
|
||||
kfree(pks);
|
||||
key_put(key);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void handle_error(void)
|
||||
{
|
||||
int mode = verity_mode();
|
||||
if (mode == DM_VERITY_MODE_RESTART) {
|
||||
DMERR("triggering restart");
|
||||
kernel_restart("dm-verity device corrupted");
|
||||
} else {
|
||||
DMERR("Mounting verity root failed");
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool test_mult_overflow(sector_t a, u32 b)
|
||||
{
|
||||
sector_t r = (sector_t)~0ULL;
|
||||
|
||||
sector_div(r, b);
|
||||
return a > r;
|
||||
}
|
||||
|
||||
static int add_as_linear_device(struct dm_target *ti, char *dev)
|
||||
{
|
||||
/*Move to linear mapping defines*/
|
||||
char *linear_table_args[DM_LINEAR_ARGS] = {dev,
|
||||
DM_LINEAR_TARGET_OFFSET};
|
||||
int err = 0;
|
||||
|
||||
android_verity_target.dtr = dm_linear_dtr,
|
||||
android_verity_target.map = dm_linear_map,
|
||||
android_verity_target.status = dm_linear_status,
|
||||
android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl,
|
||||
android_verity_target.iterate_devices = dm_linear_iterate_devices,
|
||||
android_verity_target.io_hints = NULL;
|
||||
|
||||
err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args);
|
||||
|
||||
if (!err) {
|
||||
DMINFO("Added android-verity as a linear target");
|
||||
target_added = true;
|
||||
} else
|
||||
DMERR("Failed to add android-verity as linear target");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Target parameters:
|
||||
* <key id> Key id of the public key in the system keyring.
|
||||
* Verity metadata's signature would be verified against
|
||||
* this. If the key id contains spaces, replace them
|
||||
* with '#'.
|
||||
* <block device> The block device for which dm-verity is being setup.
|
||||
*/
|
||||
static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
{
|
||||
dev_t uninitialized_var(dev);
|
||||
struct android_metadata *metadata = NULL;
|
||||
int err = 0, i, mode;
|
||||
char *key_id, *table_ptr, dummy, *target_device,
|
||||
*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
|
||||
/* One for specifying number of opt args and one for mode */
|
||||
sector_t data_sectors;
|
||||
u32 data_block_size;
|
||||
unsigned int no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS;
|
||||
struct fec_header uninitialized_var(fec);
|
||||
struct fec_ecc_metadata uninitialized_var(ecc);
|
||||
char buf[FEC_ARG_LENGTH], *buf_ptr;
|
||||
unsigned long long tmpll;
|
||||
u64 uninitialized_var(device_size);
|
||||
|
||||
if (argc == 1) {
|
||||
/* Use the default keyid */
|
||||
if (default_verity_key_id())
|
||||
key_id = veritykeyid;
|
||||
else if (!is_eng()) {
|
||||
DMERR("veritykeyid= is not set");
|
||||
handle_error();
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (argc == 2)
|
||||
key_id = argv[1];
|
||||
else {
|
||||
DMERR("Incorrect number of arguments");
|
||||
handle_error();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
target_device = argv[0];
|
||||
|
||||
dev = name_to_dev_t(target_device);
|
||||
if (!dev) {
|
||||
DMERR("no dev found for %s", target_device);
|
||||
handle_error();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_eng()) {
|
||||
err = find_size(dev, &device_size);
|
||||
if (err) {
|
||||
DMERR("error finding bdev size");
|
||||
handle_error();
|
||||
return err;
|
||||
}
|
||||
|
||||
ti->len = device_size;
|
||||
err = add_as_linear_device(ti, target_device);
|
||||
if (err) {
|
||||
handle_error();
|
||||
return err;
|
||||
}
|
||||
verity_enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
strreplace(key_id, '#', ' ');
|
||||
|
||||
DMINFO("key:%s dev:%s", key_id, target_device);
|
||||
|
||||
if (extract_fec_header(dev, &fec, &ecc)) {
|
||||
DMERR("Error while extracting fec header");
|
||||
handle_error();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
|
||||
|
||||
if (err) {
|
||||
DMERR("Error while extracting metadata");
|
||||
handle_error();
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
if (verity_enabled) {
|
||||
err = verify_verity_signature(key_id, metadata);
|
||||
|
||||
if (err) {
|
||||
DMERR("Signature verification failed");
|
||||
handle_error();
|
||||
goto free_metadata;
|
||||
} else
|
||||
DMINFO("Signature verification success");
|
||||
}
|
||||
|
||||
table_ptr = metadata->verity_table;
|
||||
|
||||
for (i = 0; i < VERITY_TABLE_ARGS; i++) {
|
||||
verity_table_args[i] = strsep(&table_ptr, " ");
|
||||
if (verity_table_args[i] == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != VERITY_TABLE_ARGS) {
|
||||
DMERR("Verity table not in the expected format");
|
||||
err = -EINVAL;
|
||||
handle_error();
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
if (sscanf(verity_table_args[5], "%llu%c", &tmpll, &dummy)
|
||||
!= 1) {
|
||||
DMERR("Verity table not in the expected format");
|
||||
handle_error();
|
||||
err = -EINVAL;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
if (tmpll > ULONG_MAX) {
|
||||
DMERR("<num_data_blocks> too large. Forgot to turn on CONFIG_LBDAF?");
|
||||
handle_error();
|
||||
err = -EINVAL;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
data_sectors = tmpll;
|
||||
|
||||
if (sscanf(verity_table_args[3], "%u%c", &data_block_size, &dummy)
|
||||
!= 1) {
|
||||
DMERR("Verity table not in the expected format");
|
||||
handle_error();
|
||||
err = -EINVAL;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
if (test_mult_overflow(data_sectors, data_block_size >>
|
||||
SECTOR_SHIFT)) {
|
||||
DMERR("data_sectors too large");
|
||||
handle_error();
|
||||
err = -EOVERFLOW;
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
data_sectors *= data_block_size >> SECTOR_SHIFT;
|
||||
DMINFO("Data sectors %llu", (unsigned long long)data_sectors);
|
||||
|
||||
/* update target length */
|
||||
ti->len = data_sectors;
|
||||
|
||||
/* Setup linear target and free */
|
||||
if (!verity_enabled) {
|
||||
err = add_as_linear_device(ti, target_device);
|
||||
goto free_metadata;
|
||||
}
|
||||
|
||||
/*substitute data_dev and hash_dev*/
|
||||
verity_table_args[1] = target_device;
|
||||
verity_table_args[2] = target_device;
|
||||
|
||||
mode = verity_mode();
|
||||
|
||||
if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) {
|
||||
if (mode) {
|
||||
err = snprintf(buf, FEC_ARG_LENGTH,
|
||||
"%u %s " VERITY_TABLE_OPT_FEC_FORMAT,
|
||||
1 + VERITY_TABLE_OPT_FEC_ARGS,
|
||||
mode == DM_VERITY_MODE_RESTART ?
|
||||
VERITY_TABLE_OPT_RESTART :
|
||||
VERITY_TABLE_OPT_LOGGING,
|
||||
target_device,
|
||||
ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
|
||||
ecc.roots);
|
||||
} else {
|
||||
err = snprintf(buf, FEC_ARG_LENGTH,
|
||||
"%u " VERITY_TABLE_OPT_FEC_FORMAT,
|
||||
VERITY_TABLE_OPT_FEC_ARGS, target_device,
|
||||
ecc.start / FEC_BLOCK_SIZE, ecc.blocks,
|
||||
ecc.roots);
|
||||
}
|
||||
} else if (mode) {
|
||||
err = snprintf(buf, FEC_ARG_LENGTH,
|
||||
"2 " VERITY_TABLE_OPT_IGNZERO " %s",
|
||||
mode == DM_VERITY_MODE_RESTART ?
|
||||
VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING);
|
||||
} else {
|
||||
err = snprintf(buf, FEC_ARG_LENGTH, "1 %s",
|
||||
"ignore_zero_blocks");
|
||||
}
|
||||
|
||||
if (err < 0 || err >= FEC_ARG_LENGTH)
|
||||
goto free_metadata;
|
||||
|
||||
buf_ptr = buf;
|
||||
|
||||
for (i = VERITY_TABLE_ARGS; i < (VERITY_TABLE_ARGS +
|
||||
VERITY_TABLE_OPT_FEC_ARGS + 2); i++) {
|
||||
verity_table_args[i] = strsep(&buf_ptr, " ");
|
||||
if (verity_table_args[i] == NULL) {
|
||||
no_of_args = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = verity_ctr(ti, no_of_args, verity_table_args);
|
||||
|
||||
if (err)
|
||||
DMERR("android-verity failed to mount as verity target");
|
||||
else {
|
||||
target_added = true;
|
||||
DMINFO("android-verity mounted as verity target");
|
||||
}
|
||||
|
||||
free_metadata:
|
||||
if (metadata) {
|
||||
kfree(metadata->header);
|
||||
kfree(metadata->verity_table);
|
||||
}
|
||||
kfree(metadata);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init dm_android_verity_init(void)
|
||||
{
|
||||
int r;
|
||||
struct dentry *file;
|
||||
|
||||
r = dm_register_target(&android_verity_target);
|
||||
if (r < 0)
|
||||
DMERR("register failed %d", r);
|
||||
|
||||
/* Tracks the status of the last added target */
|
||||
debug_dir = debugfs_create_dir("android_verity", NULL);
|
||||
|
||||
if (IS_ERR_OR_NULL(debug_dir)) {
|
||||
DMERR("Cannot create android_verity debugfs directory: %ld",
|
||||
PTR_ERR(debug_dir));
|
||||
goto end;
|
||||
}
|
||||
|
||||
file = debugfs_create_bool("target_added", S_IRUGO, debug_dir,
|
||||
&target_added);
|
||||
|
||||
if (IS_ERR_OR_NULL(file)) {
|
||||
DMERR("Cannot create android_verity debugfs directory: %ld",
|
||||
PTR_ERR(debug_dir));
|
||||
debugfs_remove_recursive(debug_dir);
|
||||
goto end;
|
||||
}
|
||||
|
||||
file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir,
|
||||
&verity_enabled);
|
||||
|
||||
if (IS_ERR_OR_NULL(file)) {
|
||||
DMERR("Cannot create android_verity debugfs directory: %ld",
|
||||
PTR_ERR(debug_dir));
|
||||
debugfs_remove_recursive(debug_dir);
|
||||
}
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit dm_android_verity_exit(void)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(debug_dir))
|
||||
debugfs_remove_recursive(debug_dir);
|
||||
|
||||
dm_unregister_target(&android_verity_target);
|
||||
}
|
||||
|
||||
module_init(dm_android_verity_init);
|
||||
module_exit(dm_android_verity_exit);
|
124
drivers/md/dm-android-verity.h
Normal file
124
drivers/md/dm-android-verity.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_ANDROID_VERITY_H
|
||||
#define DM_ANDROID_VERITY_H
|
||||
|
||||
#include <crypto/sha.h>
|
||||
|
||||
#define RSANUMBYTES 256
|
||||
#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001
|
||||
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56
|
||||
#define VERITY_METADATA_VERSION 0
|
||||
#define VERITY_STATE_DISABLE 1
|
||||
#define DATA_BLOCK_SIZE (4 * 1024)
|
||||
#define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE)
|
||||
#define VERITY_TABLE_ARGS 10
|
||||
#define VERITY_COMMANDLINE_PARAM_LENGTH 20
|
||||
#define BUILD_VARIANT 20
|
||||
|
||||
/*
|
||||
* <subject>:<sha1-id> is the format for the identifier.
|
||||
* subject can either be the Common Name(CN) + Organization Name(O) or
|
||||
* just the CN if the it is prefixed with O
|
||||
* From https://tools.ietf.org/html/rfc5280#appendix-A
|
||||
* ub-organization-name-length INTEGER ::= 64
|
||||
* ub-common-name-length INTEGER ::= 64
|
||||
*
|
||||
* http://lxr.free-electrons.com/source/crypto/asymmetric_keys/x509_cert_parser.c?v=3.9#L278
|
||||
* ctx->o_size + 2 + ctx->cn_size + 1
|
||||
* + 41 characters for ":" and sha1 id
|
||||
* 64 + 2 + 64 + 1 + 1 + 40 (172)
|
||||
* setting VERITY_DEFAULT_KEY_ID_LENGTH to 200 characters.
|
||||
*/
|
||||
#define VERITY_DEFAULT_KEY_ID_LENGTH 200
|
||||
|
||||
#define FEC_MAGIC 0xFECFECFE
|
||||
#define FEC_BLOCK_SIZE (4 * 1024)
|
||||
#define FEC_VERSION 0
|
||||
#define FEC_RSM 255
|
||||
#define FEC_ARG_LENGTH 300
|
||||
|
||||
#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
|
||||
#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
|
||||
#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
|
||||
|
||||
#define VERITY_TABLE_OPT_FEC_FORMAT \
|
||||
"use_fec_from_device %s fec_start %llu fec_blocks %llu fec_roots %u ignore_zero_blocks"
|
||||
#define VERITY_TABLE_OPT_FEC_ARGS 9
|
||||
|
||||
#define VERITY_DEBUG 0
|
||||
|
||||
#define DM_MSG_PREFIX "android-verity"
|
||||
|
||||
#define DM_LINEAR_ARGS 2
|
||||
#define DM_LINEAR_TARGET_OFFSET "0"
|
||||
|
||||
/*
|
||||
* There can be two formats.
|
||||
* if fec is present
|
||||
* <data_blocks> <verity_tree> <verity_metdata_32K><fec_data><fec_data_4K>
|
||||
* if fec is not present
|
||||
* <data_blocks> <verity_tree> <verity_metdata_32K>
|
||||
*/
|
||||
/* TODO: rearrange structure to reduce memory holes
|
||||
* depends on userspace change.
|
||||
*/
|
||||
struct fec_header {
|
||||
__le32 magic;
|
||||
__le32 version;
|
||||
__le32 size;
|
||||
__le32 roots;
|
||||
__le32 fec_size;
|
||||
__le64 inp_size;
|
||||
u8 hash[SHA256_DIGEST_SIZE];
|
||||
};
|
||||
|
||||
struct android_metadata_header {
|
||||
__le32 magic_number;
|
||||
__le32 protocol_version;
|
||||
char signature[RSANUMBYTES];
|
||||
__le32 table_length;
|
||||
};
|
||||
|
||||
struct android_metadata {
|
||||
struct android_metadata_header *header;
|
||||
char *verity_table;
|
||||
};
|
||||
|
||||
struct fec_ecc_metadata {
|
||||
bool valid;
|
||||
u32 roots;
|
||||
u64 blocks;
|
||||
u64 rounds;
|
||||
u64 start;
|
||||
};
|
||||
|
||||
struct bio_read {
|
||||
struct page **page_io;
|
||||
int number_of_pages;
|
||||
};
|
||||
|
||||
extern struct target_type linear_target;
|
||||
|
||||
extern void dm_linear_dtr(struct dm_target *ti);
|
||||
extern int dm_linear_map(struct dm_target *ti, struct bio *bio);
|
||||
extern void dm_linear_status(struct dm_target *ti, status_type_t type,
|
||||
unsigned status_flags, char *result, unsigned maxlen);
|
||||
extern int dm_linear_prepare_ioctl(struct dm_target *ti,
|
||||
struct block_device **bdev, fmode_t *mode);
|
||||
extern int dm_linear_iterate_devices(struct dm_target *ti,
|
||||
iterate_devices_callout_fn fn, void *data);
|
||||
extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv);
|
||||
#endif /* DM_ANDROID_VERITY_H */
|
|
@ -25,7 +25,7 @@ struct linear_c {
|
|||
/*
|
||||
* Construct a linear mapping: <dev_path> <offset>
|
||||
*/
|
||||
static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||
int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||
{
|
||||
struct linear_c *lc;
|
||||
unsigned long long tmp;
|
||||
|
@ -67,7 +67,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void linear_dtr(struct dm_target *ti)
|
||||
void dm_linear_dtr(struct dm_target *ti)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *) ti->private;
|
||||
|
||||
|
@ -92,14 +92,14 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio)
|
|||
linear_map_sector(ti, bio->bi_iter.bi_sector);
|
||||
}
|
||||
|
||||
static int linear_map(struct dm_target *ti, struct bio *bio)
|
||||
int dm_linear_map(struct dm_target *ti, struct bio *bio)
|
||||
{
|
||||
linear_map_bio(ti, bio);
|
||||
|
||||
return DM_MAPIO_REMAPPED;
|
||||
}
|
||||
|
||||
static void linear_status(struct dm_target *ti, status_type_t type,
|
||||
void dm_linear_status(struct dm_target *ti, status_type_t type,
|
||||
unsigned status_flags, char *result, unsigned maxlen)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *) ti->private;
|
||||
|
@ -116,7 +116,7 @@ static void linear_status(struct dm_target *ti, status_type_t type,
|
|||
}
|
||||
}
|
||||
|
||||
static int linear_prepare_ioctl(struct dm_target *ti,
|
||||
int dm_linear_prepare_ioctl(struct dm_target *ti,
|
||||
struct block_device **bdev, fmode_t *mode)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *) ti->private;
|
||||
|
@ -133,7 +133,7 @@ static int linear_prepare_ioctl(struct dm_target *ti,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int linear_iterate_devices(struct dm_target *ti,
|
||||
int dm_linear_iterate_devices(struct dm_target *ti,
|
||||
iterate_devices_callout_fn fn, void *data)
|
||||
{
|
||||
struct linear_c *lc = ti->private;
|
||||
|
@ -145,12 +145,12 @@ static struct target_type linear_target = {
|
|||
.name = "linear",
|
||||
.version = {1, 2, 1},
|
||||
.module = THIS_MODULE,
|
||||
.ctr = linear_ctr,
|
||||
.dtr = linear_dtr,
|
||||
.map = linear_map,
|
||||
.status = linear_status,
|
||||
.prepare_ioctl = linear_prepare_ioctl,
|
||||
.iterate_devices = linear_iterate_devices,
|
||||
.ctr = dm_linear_ctr,
|
||||
.dtr = dm_linear_dtr,
|
||||
.map = dm_linear_map,
|
||||
.status = dm_linear_status,
|
||||
.prepare_ioctl = dm_linear_prepare_ioctl,
|
||||
.iterate_devices = dm_linear_iterate_devices,
|
||||
};
|
||||
|
||||
int __init dm_linear_init(void)
|
||||
|
|
|
@ -442,6 +442,13 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
|||
if (!verity_fec_is_enabled(v))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) {
|
||||
DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fio->level++;
|
||||
|
||||
if (type == DM_VERITY_BLOCK_TYPE_METADATA)
|
||||
block += v->data_blocks;
|
||||
|
||||
|
@ -456,9 +463,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
|||
*/
|
||||
|
||||
offset = block << v->data_dev_block_bits;
|
||||
|
||||
res = offset;
|
||||
div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
|
||||
res = div64_u64(offset, v->fec->rounds << v->data_dev_block_bits);
|
||||
|
||||
/*
|
||||
* The base RS block we can feed to the interleaver to find out all
|
||||
|
@ -475,7 +480,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
|||
if (r < 0) {
|
||||
r = fec_decode_rsb(v, io, fio, rsb, offset, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (dest)
|
||||
|
@ -485,6 +490,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
|
|||
r = verity_for_bv_block(v, io, iter, fec_bv_copy);
|
||||
}
|
||||
|
||||
done:
|
||||
fio->level--;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -525,6 +532,7 @@ void verity_fec_init_io(struct dm_verity_io *io)
|
|||
memset(fio->bufs, 0, sizeof(fio->bufs));
|
||||
fio->nbufs = 0;
|
||||
fio->output = NULL;
|
||||
fio->level = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -680,7 +688,8 @@ static struct attribute *fec_attrs[] = {
|
|||
|
||||
static struct kobj_type fec_ktype = {
|
||||
.sysfs_ops = &kobj_sysfs_ops,
|
||||
.default_attrs = fec_attrs
|
||||
.default_attrs = fec_attrs,
|
||||
.release = dm_kobject_release
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#define DM_VERITY_FEC_BUF_MAX \
|
||||
(1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS))
|
||||
|
||||
/* maximum recursion level for verity_fec_decode */
|
||||
#define DM_VERITY_FEC_MAX_RECURSION 4
|
||||
|
||||
#define DM_VERITY_OPT_FEC_DEV "use_fec_from_device"
|
||||
#define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks"
|
||||
#define DM_VERITY_OPT_FEC_START "fec_start"
|
||||
|
@ -61,6 +64,7 @@ struct dm_verity_fec_io {
|
|||
unsigned nbufs; /* number of buffers allocated */
|
||||
u8 *output; /* buffer for corrected output */
|
||||
size_t output_pos;
|
||||
unsigned level; /* recursion level */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DM_VERITY_FEC
|
||||
|
|
|
@ -551,7 +551,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
|
|||
* Bio map function. It allocates dm_verity_io structure and bio vector and
|
||||
* fills them. Then it issues prefetches and the I/O.
|
||||
*/
|
||||
static int verity_map(struct dm_target *ti, struct bio *bio)
|
||||
int verity_map(struct dm_target *ti, struct bio *bio)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
struct dm_verity_io *io;
|
||||
|
@ -596,7 +596,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
|
|||
/*
|
||||
* Status: V (valid) or C (corruption found)
|
||||
*/
|
||||
static void verity_status(struct dm_target *ti, status_type_t type,
|
||||
void verity_status(struct dm_target *ti, status_type_t type,
|
||||
unsigned status_flags, char *result, unsigned maxlen)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
|
@ -656,7 +656,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
|
|||
}
|
||||
}
|
||||
|
||||
static int verity_prepare_ioctl(struct dm_target *ti,
|
||||
int verity_prepare_ioctl(struct dm_target *ti,
|
||||
struct block_device **bdev, fmode_t *mode)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
|
@ -669,7 +669,7 @@ static int verity_prepare_ioctl(struct dm_target *ti,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int verity_iterate_devices(struct dm_target *ti,
|
||||
int verity_iterate_devices(struct dm_target *ti,
|
||||
iterate_devices_callout_fn fn, void *data)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
|
@ -677,7 +677,7 @@ static int verity_iterate_devices(struct dm_target *ti,
|
|||
return fn(ti, v->data_dev, v->data_start, ti->len, data);
|
||||
}
|
||||
|
||||
static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
|
||||
void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
|
||||
|
@ -690,7 +690,7 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
|
|||
blk_limits_io_min(limits, limits->logical_block_size);
|
||||
}
|
||||
|
||||
static void verity_dtr(struct dm_target *ti)
|
||||
void verity_dtr(struct dm_target *ti)
|
||||
{
|
||||
struct dm_verity *v = ti->private;
|
||||
|
||||
|
@ -817,7 +817,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
|
|||
* <digest>
|
||||
* <salt> Hex string or "-" if no salt.
|
||||
*/
|
||||
static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
{
|
||||
struct dm_verity *v;
|
||||
struct dm_arg_set as;
|
||||
|
|
|
@ -126,4 +126,14 @@ extern int verity_hash(struct dm_verity *v, struct shash_desc *desc,
|
|||
extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
|
||||
sector_t block, u8 *digest, bool *is_zero);
|
||||
|
||||
extern void verity_status(struct dm_target *ti, status_type_t type,
|
||||
unsigned status_flags, char *result, unsigned maxlen);
|
||||
extern int verity_prepare_ioctl(struct dm_target *ti,
|
||||
struct block_device **bdev, fmode_t *mode);
|
||||
extern int verity_iterate_devices(struct dm_target *ti,
|
||||
iterate_devices_callout_fn fn, void *data);
|
||||
extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits);
|
||||
extern void verity_dtr(struct dm_target *ti);
|
||||
extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv);
|
||||
extern int verity_map(struct dm_target *ti, struct bio *bio);
|
||||
#endif /* DM_VERITY_H */
|
||||
|
|
|
@ -13,6 +13,9 @@ void mount_block_root(char *name, int flags);
|
|||
void mount_root(void);
|
||||
extern int root_mountflags;
|
||||
|
||||
struct dm_table;
|
||||
static inline void dm_table_put(struct dm_table *t) { }
|
||||
|
||||
static inline int create_dev(char *name, dev_t dev)
|
||||
{
|
||||
sys_unlink(name);
|
||||
|
|
Loading…
Add table
Reference in a new issue