From 352cda03027b2a24d503522d48880477b1dcdf48 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Wed, 19 Feb 2025 15:06:47 +0100 Subject: [PATCH] integrity: Detect PI/DIF capable devices in inline mode. And print better error if not. --- lib/internal.h | 2 ++ lib/setup.c | 12 ++++++++++++ lib/utils_device.c | 16 ++++++++++++++++ lib/utils_devpath.c | 23 +++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/lib/internal.h b/lib/internal.h index e7809b32..0202b40c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -116,6 +116,7 @@ int device_is_identical(struct device *device1, struct device *device2); int device_is_rotational(struct device *device); int device_is_dax(struct device *device); int device_is_zoned(struct device *device); +int device_is_nop_dif(struct device *device, uint32_t *tag_size); size_t device_alignment(struct device *device); int device_direct_io(const struct device *device); int device_fallocate(struct device *device, uint64_t size); @@ -167,6 +168,7 @@ char *crypt_lookup_dev(const char *dev_id); int crypt_dev_is_rotational(int major, int minor); int crypt_dev_is_dax(int major, int minor); int crypt_dev_is_zoned(int major, int minor); +int crypt_dev_is_nop_dif(int major, int minor, uint32_t *tag_size); int crypt_dev_is_partition(const char *dev_path); char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t size); int crypt_dev_get_partition_number(const char *dev_path); diff --git a/lib/setup.c b/lib/setup.c index a605b905..49d76459 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -3068,6 +3068,7 @@ int crypt_format_inline(struct crypt_device *cd, void *params) { const struct crypt_params_integrity *iparams; + uint32_t device_tag_size; struct device *idevice; size_t sector_size, required_sector_size; int r; @@ -3113,6 +3114,17 @@ int crypt_format_inline(struct crypt_device *cd, return -EINVAL; } + if (!device_is_nop_dif(crypt_metadata_device(cd), &device_tag_size)) { + log_err(cd, _("Device %s does not provide inline integrity data fields."), mdata_device_path(cd)); + return -EINVAL; + } + + if (device_tag_size < iparams->tag_size) { + log_err(cd, _("Inline tag size %" PRIu32 " [bytes] is larger than %" PRIu32 " provided by device %s."), + iparams->tag_size, device_tag_size, mdata_device_path(cd)); + return -EINVAL; + } + if (isINTEGRITY(type)) r = _crypt_format_integrity(cd, uuid, params, volume_key, volume_key_size, true); else diff --git a/lib/utils_device.c b/lib/utils_device.c index 84acf29b..9bc2fcb4 100644 --- a/lib/utils_device.c +++ b/lib/utils_device.c @@ -1017,6 +1017,22 @@ int device_is_zoned(struct device *device) return crypt_dev_is_zoned(major(st.st_rdev), minor(st.st_rdev)); } +int device_is_nop_dif(struct device *device, uint32_t *tag_size) +{ + struct stat st; + + if (!device) + return -EINVAL; + + if (stat(device_path(device), &st) < 0) + return -EINVAL; + + if (!S_ISBLK(st.st_mode)) + return 0; + + return crypt_dev_is_nop_dif(major(st.st_rdev), minor(st.st_rdev), tag_size); +} + size_t device_alignment(struct device *device) { int devfd; diff --git a/lib/utils_devpath.c b/lib/utils_devpath.c index a7fb582e..28039f0d 100644 --- a/lib/utils_devpath.c +++ b/lib/utils_devpath.c @@ -262,6 +262,29 @@ int crypt_dev_is_zoned(int major, int minor) return strncmp(buf, "none", 4) ? 1 : 0; } +int crypt_dev_is_nop_dif(int major, int minor, uint32_t *tag_size) +{ + char buf[64] = {}; + uint64_t val = 0; + + if (!_sysfs_get_string(major, minor, buf, sizeof(buf), "integrity/format")) + return 0; + + if (strncmp(buf, "nop", 3)) + return 0; + + /* this field is currently supported only for NVMe */ + _sysfs_get_uint64(major, minor, &val, "metadata_bytes"); + + /* tag_size should be 0, but it is set by dm-integrity, try it as a fallback */ + if (val == 0) + _sysfs_get_uint64(major, minor, &val, "integrity/tag_size"); + + /* we can still return 0 and support metadata, caller must handle it */ + *tag_size = (uint32_t)val; + return 1; +} + int crypt_dev_is_partition(const char *dev_path) { uint64_t val;