From 448fca1fdf0e925ea85b59e81698144e0cb39aa9 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Thu, 16 May 2019 15:02:54 +0200 Subject: [PATCH] Integritysetup: implement new bitmap mode. --- lib/integrity/integrity.c | 9 ++++++--- lib/integrity/integrity.h | 7 +++++-- lib/libcryptsetup.h | 10 ++++++++-- lib/libdevmapper.c | 37 +++++++++++++++++++++++++++++++------ lib/utils_dm.h | 1 + man/integritysetup.8 | 15 +++++++++++++++ src/integritysetup.c | 39 ++++++++++++++++++++++++++++++++------- 7 files changed, 98 insertions(+), 20 deletions(-) diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c index 3243d8df..b9d1630c 100644 --- a/lib/integrity/integrity.c +++ b/lib/integrity/integrity.c @@ -41,7 +41,8 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd, if (read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) || memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) || - (sb->version != SB_VERSION_1 && sb->version != SB_VERSION_2)) { + (sb->version != SB_VERSION_1 && sb->version != SB_VERSION_2 && + sb->version != SB_VERSION_3)) { log_std(cd, "No integrity superblock detected on %s.\n", device_path(device)); r = -EINVAL; @@ -90,9 +91,11 @@ int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offs log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block); if (sb.version == SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING)) log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector); - log_std(cd, "flags %s%s\n", + log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit); + log_std(cd, "flags %s%s%s\n", sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "", - sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : ""); + sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "", + sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : ""); return 0; } diff --git a/lib/integrity/integrity.h b/lib/integrity/integrity.h index 7a4b4c2f..ffbcbf7a 100644 --- a/lib/integrity/integrity.h +++ b/lib/integrity/integrity.h @@ -33,9 +33,11 @@ struct crypt_dm_active_device; #define SB_MAGIC "integrt" #define SB_VERSION_1 1 #define SB_VERSION_2 2 +#define SB_VERSION_3 3 #define SB_FLAG_HAVE_JOURNAL_MAC (1 << 0) #define SB_FLAG_RECALCULATING (1 << 1) /* V2 only */ +#define SB_FLAG_DIRTY_BITMAP (1 << 2) /* V3 only */ struct superblock { uint8_t magic[8]; @@ -46,8 +48,9 @@ struct superblock { uint64_t provided_data_sectors; uint32_t flags; uint8_t log2_sectors_per_block; - uint8_t pad[3]; - uint64_t recalc_sector; /* V2 only */ + uint8_t log2_blocks_per_bitmap_bit; /* V3 only */ + uint8_t pad[2]; + uint64_t recalc_sector; /* V2 only */ } __attribute__ ((packed)); int INTEGRITY_read_sb(struct crypt_device *cd, struct crypt_params_integrity *params); diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index ff42b8f9..d38ccdd4 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -546,11 +546,15 @@ struct crypt_params_tcrypt { * * @see crypt_format, crypt_load * + * @note In bitmap tracking mode, the journal is implicitly disabled. + * As an ugly workaround for compatibility, journal_watermark is overloaded + * to mean 512-bytes sectors-per-bit and journal_commit_time means bitmap flush time. + * All other journal parameters are not applied in the bitmap mode. */ struct crypt_params_integrity { uint64_t journal_size; /**< size of journal in bytes */ - unsigned int journal_watermark; /**< journal flush watermark in percents */ - unsigned int journal_commit_time; /**< journal commit time in ms */ + unsigned int journal_watermark; /**< journal flush watermark in percents; in bitmap mode sectors-per-bit */ + unsigned int journal_commit_time; /**< journal commit time (or bitmap flush time) in ms */ uint32_t interleave_sectors; /**< number of interleave sectors (power of two) */ uint32_t tag_size; /**< tag size per-sector in bytes */ uint32_t sector_size; /**< sector size in bytes */ @@ -1059,6 +1063,8 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); #define CRYPT_ACTIVATE_REFRESH (1 << 18) /** Use global lock to serialize memory hard KDF on activation (OOM workaround) */ #define CRYPT_ACTIVATE_SERIALIZE_MEMORY_HARD_PBKDF (1 << 19) +/** dm-integrity: direct writes, use bitmap to track dirty sectors */ +#define CRYPT_ACTIVATE_NO_JOURNAL_BITMAP (1 << 20) /** * Active device runtime attributes diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 5b917ccd..74009619 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -215,6 +215,9 @@ static void _dm_set_integrity_compat(struct crypt_device *cd, if (_dm_satisfies_version(1, 2, 0, integrity_maj, integrity_min, integrity_patch)) _dm_flags |= DM_INTEGRITY_RECALC_SUPPORTED; + if (_dm_satisfies_version(1, 3, 0, integrity_maj, integrity_min, integrity_patch)) + _dm_flags |= DM_INTEGRITY_BITMAP_SUPPORTED; + _dm_integrity_checked = true; } @@ -709,7 +712,7 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags (tgt->u.integrity.journal_crypt_key ? tgt->u.integrity.journal_crypt_key->keylength * 2 : 0) + (tgt->u.integrity.integrity ? strlen(tgt->u.integrity.integrity) : 0) + (tgt->u.integrity.journal_integrity ? strlen(tgt->u.integrity.journal_integrity) : 0) + - (tgt->u.integrity.journal_crypt ? strlen(tgt->u.integrity.journal_crypt) : 0) + 128; + (tgt->u.integrity.journal_crypt ? strlen(tgt->u.integrity.journal_crypt) : 0) + 128; params = crypt_safe_alloc(max_size); if (!params) @@ -724,13 +727,17 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags } if (tgt->u.integrity.journal_watermark) { num_options++; - snprintf(feature, sizeof(feature), "journal_watermark:%u ", + snprintf(feature, sizeof(feature), + /* bitmap oveloaded values */ + (flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) ? "sectors_per_bit:%u " : "journal_watermark:%u ", tgt->u.integrity.journal_watermark); strncat(features, feature, sizeof(features) - strlen(features) - 1); } if (tgt->u.integrity.journal_commit_time) { num_options++; - snprintf(feature, sizeof(feature), "commit_time:%u ", + snprintf(feature, sizeof(feature), + /* bitmap oveloaded values */ + (flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) ? "bitmap_flush_interval:%u " : "commit_time:%u ", tgt->u.integrity.journal_commit_time); strncat(features, feature, sizeof(features) - strlen(features) - 1); } @@ -824,7 +831,9 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags strncat(features, feature, sizeof(features) - strlen(features) - 1); } - if (flags & CRYPT_ACTIVATE_RECOVERY) + if (flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) + mode = 'B'; + else if (flags & CRYPT_ACTIVATE_RECOVERY) mode = 'R'; else if (flags & CRYPT_ACTIVATE_NO_JOURNAL) mode = 'D'; @@ -1528,6 +1537,10 @@ int dm_create_device(struct crypt_device *cd, const char *name, if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) && !(dmt_flags & DM_INTEGRITY_RECALC_SUPPORTED)) log_err(cd, _("Requested automatic recalculation of integrity tags is not supported.")); + + if (r == -EINVAL && dmd->segment.type == DM_INTEGRITY && (dmd->flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) && + !(dmt_flags & DM_INTEGRITY_BITMAP_SUPPORTED)) + log_err(cd, _("Requested dm-integtity bitmap mode is not supported.")); out: dm_exit_context(); return r; @@ -2165,12 +2178,16 @@ static int _dm_target_query_integrity(struct crypt_device *cd, /* journal */ c = toupper(*(++params)); - if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R')) + if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R' && c != 'B')) goto err; if (c == 'D') *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; if (c == 'R') *act_flags |= CRYPT_ACTIVATE_RECOVERY; + if (c == 'B') { + *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL; + *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP; + } tgt->u.integrity.sector_size = SECTOR_SIZE; @@ -2192,7 +2209,15 @@ static int _dm_target_query_integrity(struct crypt_device *cd, tgt->u.integrity.journal_size = val * SECTOR_SIZE; else if (sscanf(arg, "journal_watermark:%u", &val) == 1) tgt->u.integrity.journal_watermark = val; - else if (sscanf(arg, "commit_time:%u", &val) == 1) + else if (sscanf(arg, "sectors_per_bit:%" PRIu64, &val64) == 1) { + if (val64 > UINT_MAX) + goto err; + /* overrloaded value for bitmap mode */ + tgt->u.integrity.journal_watermark = (unsigned int)val64; + } else if (sscanf(arg, "commit_time:%u", &val) == 1) + tgt->u.integrity.journal_commit_time = val; + else if (sscanf(arg, "bitmap_flush_interval:%u", &val) == 1) + /* overrloaded value for bitmap mode */ tgt->u.integrity.journal_commit_time = val; else if (sscanf(arg, "interleave_sectors:%u", &val) == 1) tgt->u.integrity.interleave_sectors = val; diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 6ba7a795..0ebbe160 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -62,6 +62,7 @@ static inline uint32_t act2dmflags(uint32_t act_flags) #define DM_CAPI_STRING_SUPPORTED (1 << 14) /* support for cryptoapi format cipher definition */ #define DM_DEFERRED_SUPPORTED (1 << 15) /* deferred removal of device */ #define DM_INTEGRITY_RECALC_SUPPORTED (1 << 16) /* dm-integrity automatic recalculation supported */ +#define DM_INTEGRITY_BITMAP_SUPPORTED (1 << 17) /* dm-integrity bitmap mode supported */ typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_LINEAR, DM_ERROR, DM_UNKNOWN } dm_target_type; enum tdirection { TARGET_SET = 1, TARGET_QUERY }; diff --git a/man/integritysetup.8 b/man/integritysetup.8 index 5b40b845..aa923bc1 100644 --- a/man/integritysetup.8 +++ b/man/integritysetup.8 @@ -125,6 +125,21 @@ The file with the integrity key. .TP .B "\-\-integrity\-no\-journal, \-D" Disable journal for integrity device. +.TP +.B "\-\-integrity\-bitmap\-mode. \-B" +Use alternate bitmap mode (available since Linux kernel 5.2) where dm-integrity uses bitmap +instead of a journal. If a bit in the bitmap is 1, the corresponding region's data and integrity tags +are not synchronized - if the machine crashes, the unsynchronized regions will be recalculated. +The bitmap mode is faster than the journal mode, because we don't have to write the data +twice, but it is also less reliable, because if data corruption happens +when the machine crashes, it may not be detected. +.TP +.B "\-\-bitmap\-sectors\-per\-bit SECTORS" +Number of 512-byte sectors per bitmap bit, the value must be power of two. +.TP +.B "\-\-bitmap\-flush\-time MS" +Bitmap flush time in milliseconds. +.TP \fBWARNING:\fR In case of a crash, it is possible that the data and integrity tag doesn't match diff --git a/src/integritysetup.c b/src/integritysetup.c index f451f9bc..27bf0970 100644 --- a/src/integritysetup.c +++ b/src/integritysetup.c @@ -32,7 +32,9 @@ static const char *opt_journal_size_str = NULL; static uint64_t opt_journal_size = 0; static int opt_interleave_sectors = 0; static int opt_journal_watermark = 0; +static int opt_bitmap_sectors_per_bit = 0; static int opt_journal_commit_time = 0; +static int opt_bitmap_flush_time = 0; static int opt_tag_size = 0; static int opt_sector_size = 0; static int opt_buffer_sectors = 0; @@ -55,6 +57,7 @@ static int opt_journal_crypt_key_size = 0; static int opt_integrity_nojournal = 0; static int opt_integrity_recovery = 0; +static int opt_integrity_bitmap = 0; static int opt_integrity_recalculate = 0; @@ -175,8 +178,9 @@ static int action_format(int arg) struct crypt_params_integrity params = { .journal_size = opt_journal_size, .interleave_sectors = opt_interleave_sectors, - .journal_watermark = opt_journal_watermark, - .journal_commit_time = opt_journal_commit_time, + /* in bitmap mode we have to overload these values... */ + .journal_watermark = opt_integrity_bitmap ? opt_bitmap_sectors_per_bit : opt_journal_watermark, + .journal_commit_time = opt_integrity_bitmap ? opt_bitmap_flush_time : opt_journal_commit_time, .buffer_sectors = opt_buffer_sectors, .tag_size = opt_tag_size, .sector_size = opt_sector_size ?: SECTOR_SIZE, @@ -261,8 +265,9 @@ static int action_open(int arg) { struct crypt_device *cd = NULL; struct crypt_params_integrity params = { - .journal_watermark = opt_journal_watermark, - .journal_commit_time = opt_journal_commit_time, + /* in bitmap mode we have to overload these values... */ + .journal_watermark = opt_integrity_bitmap ? opt_bitmap_sectors_per_bit : opt_journal_watermark, + .journal_commit_time = opt_integrity_bitmap ? opt_bitmap_flush_time : opt_journal_commit_time, .buffer_sectors = opt_buffer_sectors, }; uint32_t activate_flags = 0; @@ -298,10 +303,12 @@ static int action_open(int arg) params.journal_crypt = journal_crypt; } - if (opt_integrity_nojournal) + if (opt_integrity_nojournal || opt_integrity_bitmap) activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL; if (opt_integrity_recovery) activate_flags |= CRYPT_ACTIVATE_RECOVERY; + if (opt_integrity_bitmap) + activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP; if (opt_integrity_recalculate) activate_flags |= CRYPT_ACTIVATE_RECALCULATE; @@ -415,7 +422,10 @@ static int action_status(int arg) cad.flags & CRYPT_ACTIVATE_RECOVERY ? " recovery" : ""); log_std(" failures: %" PRIu64 "\n", crypt_get_active_integrity_failures(cd, action_argv[0])); - if (cad.flags & CRYPT_ACTIVATE_NO_JOURNAL) { + if (cad.flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) { + log_std(" bitmap 512-byte sectors per bit: %u\n", ip.journal_watermark); + log_std(" bitmap flush interval: %u ms\n", ip.journal_commit_time); + } if (cad.flags & CRYPT_ACTIVATE_NO_JOURNAL) { log_std(" journal: not active\n"); } else { log_std(" journal size: %" PRIu64 " bytes\n", ip.journal_size); @@ -531,6 +541,8 @@ int main(int argc, const char **argv) { "interleave-sectors", '\0', POPT_ARG_INT, &opt_interleave_sectors, 0, N_("Interleave sectors"), N_("SECTORS") }, { "journal-watermark", '\0', POPT_ARG_INT, &opt_journal_watermark, 0, N_("Journal watermark"),N_("percent") }, { "journal-commit-time",'\0', POPT_ARG_INT, &opt_journal_commit_time,0, N_("Journal commit time"), N_("ms") }, + { "bitmap-sectors-per-bit",'\0', POPT_ARG_INT,&opt_bitmap_sectors_per_bit, 0, N_("Number of 512-byte sectors per bit (bitmap mode)."), NULL }, + { "bitmap-flush-time", '\0', POPT_ARG_INT, &opt_bitmap_flush_time, 0, N_("Bitmap mode flush time"), N_("ms") }, { "tag-size", 't', POPT_ARG_INT, &opt_tag_size, 0, N_("Tag size (per-sector)"), N_("bytes") }, { "sector-size", 's', POPT_ARG_INT, &opt_sector_size, 0, N_("Sector size"), N_("bytes") }, { "buffer-sectors", '\0', POPT_ARG_INT, &opt_buffer_sectors, 0, N_("Buffers size"), N_("SECTORS") }, @@ -549,6 +561,7 @@ int main(int argc, const char **argv) { "integrity-no-journal", 'D', POPT_ARG_NONE, &opt_integrity_nojournal, 0, N_("Disable journal for integrity device"), NULL }, { "integrity-recovery-mode", 'R', POPT_ARG_NONE, &opt_integrity_recovery, 0, N_("Recovery mode (no journal, no tag checking)"), NULL }, + { "integrity-bitmap-mode", 'B', POPT_ARG_NONE, &opt_integrity_bitmap, 0, N_("Use bitmap to track changes and disable journal for integrity device"), NULL }, { "integrity-recalculate", '\0', POPT_ARG_NONE, &opt_integrity_recalculate, 0, N_("Recalculate initial tags automatically."), NULL }, POPT_TABLEEND }; @@ -635,7 +648,7 @@ int main(int argc, const char **argv) opt_journal_commit_time < 0 || opt_tag_size < 0 || opt_sector_size < 0 || opt_buffer_sectors < 0 || opt_integrity_key_size < 0 || opt_journal_integrity_key_size < 0 || - opt_journal_crypt_key_size < 0) + opt_journal_crypt_key_size < 0 || opt_bitmap_flush_time < 0 || opt_bitmap_sectors_per_bit < 0) usage(popt_context, EXIT_FAILURE, _("Negative number for option not permitted."), poptGetInvocationName(popt_context)); @@ -676,6 +689,18 @@ int main(int argc, const char **argv) usage(popt_context, EXIT_FAILURE, _("Journal encryption algorithm must be specified if journal encryption key is used."), poptGetInvocationName(popt_context)); + if (opt_integrity_recovery && opt_integrity_bitmap) + usage(popt_context, EXIT_FAILURE, _("Recovery and bitmap mode options are mutually exclusive."), + poptGetInvocationName(popt_context)); + + if (opt_integrity_bitmap && (opt_journal_integrity_key_file || opt_journal_crypt || opt_journal_watermark || opt_journal_commit_time)) + usage(popt_context, EXIT_FAILURE, _("Journal options cannot be used in bitmap mode."), + poptGetInvocationName(popt_context)); + + if (!opt_integrity_bitmap && (opt_bitmap_flush_time || opt_bitmap_sectors_per_bit)) + usage(popt_context, EXIT_FAILURE, _("Bitmap options can be used only in bitmap mode."), + poptGetInvocationName(popt_context)); + if (opt_debug) { opt_verbose = 1; crypt_set_debug_level(-1);