Integritysetup: implement new bitmap mode.

This commit is contained in:
Milan Broz
2019-05-16 15:02:54 +02:00
committed by Ondrej Kozina
parent 1923928fdc
commit 448fca1fdf
7 changed files with 98 additions and 20 deletions

View File

@@ -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;
}

View File

@@ -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,7 +48,8 @@ struct superblock {
uint64_t provided_data_sectors;
uint32_t flags;
uint8_t log2_sectors_per_block;
uint8_t pad[3];
uint8_t log2_blocks_per_bitmap_bit; /* V3 only */
uint8_t pad[2];
uint64_t recalc_sector; /* V2 only */
} __attribute__ ((packed));

View File

@@ -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

View File

@@ -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;
}
@@ -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;

View File

@@ -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 };

View File

@@ -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

View File

@@ -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);