summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2019-11-20 16:07:27 -0600
committerDavid Teigland <teigland@redhat.com>2019-12-06 14:14:57 -0600
commitbf71ad27198b55fd443a18445db651556c46de97 (patch)
tree22ff65261f196cee214cbc5555b17e4fa595f617
parentec71df6fec481b688a7c4db3171a451c18dbdc52 (diff)
downloadlvm2-dev-dct-integrity10.tar.gz
dm-integrity supportdev-dct-integrity10
dm-integrity stores checksums of the data written to an LV, and returns an error if data read from the LV does not match the previously saved checksum. Create a linear LV with a dm-integrity layer above it: lvcreate --type integrity --integrity String [options] Create a raid1 LV with a dm-integrity layer over each raid image: lvcreate --type raid1 --integrity String [options] Integrity metadata: The --integrity String specifies if the dm-integrity metadata (checksums) should be interleaved with data blocks, or written to a separate external LV. --integrity external (default) Store integrity metadata on a separate LV. Allows removing integrity from the LV later. --integrity internal Store integrity metadata interleaved with data on the same LV. Around 1% of the LV size will be used for integrity metadata. (Not currently allowed with raid1.) --integrity y Enable default integrity settings (external). Command variations: lvcreate --type integrity -n Name -L Size VG [Uses integrity external, the default.] lvcreate --integrity external -n Name -L Size VG [Uses type integrity, which is implied.] lvcreate --integrity y -n Name -L Size VG [Uses integrity external, the default, and uses type integrity, which is implied.] lvcreate --integrity internal -n Name -L Size VG [Uses type integrity, which is implied.] lvcreate --type raid1 --integrity y -m Num -n Name -L Size VG [Uses type integrity for each raid image.] lvconvert --integrity none|n LV [Removes external integrity.] Options: --integritymetadata LV Use the specified LV for external metadata. Allows specific device placement of metadata. Without this option, the command creates a hidden LV (with an _imeta suffix) to hold the metadata. (Not usable with raid1+integrity.) --integritysettings String set dm-integrity parameters, e.g. to use a journal instead of bitmap, --integritysettings "mode=J". Example: $ lvcreate --integrity external -n lvex -L1G vg $ lvs -a vg LV VG Attr LSize Origin lvex vg -wi-a----- 1.00g [lvex_iorig] [lvex_imeta] vg -wi-ao---- 12.00m [lvex_iorig] vg -wi-ao---- 1.00g $ lvcreate --integrity internal -n lvin -L1G vg $ lvs -a vg LV VG Attr LSize Origin lvin vg -wi-a----- 1.00g [lvin_iorig] [lvin_iorig] vg -wi-ao---- 1.00g $ lvcreate --type raid1 --integrity y -m 1 -n lver -L1G vg $ lvs -a vg LV VG Attr LSize Origin lver dd rwi-a-r--- 1.00g [lver_rimage_0] dd gwi-aor--- 1.00g [lver_rimage_0_iorig] [lver_rimage_0_imeta] dd ewi-ao---- 12.00m [lver_rimage_0_iorig] dd -wi-ao---- 1.00g [lver_rimage_1] dd gwi-aor--- 1.00g [lver_rimage_1_iorig] [lver_rimage_1_imeta] dd ewi-ao---- 12.00m [lver_rimage_1_iorig] dd -wi-ao---- 1.00g [lver_rmeta_0] dd ewi-aor--- 4.00m [lver_rmeta_1] dd ewi-aor--- 4.00m
-rw-r--r--device_mapper/all.h39
-rw-r--r--device_mapper/libdm-deptree.c130
-rw-r--r--device_mapper/libdm-targets.c29
-rw-r--r--lib/Makefile.in2
-rw-r--r--lib/activate/activate.c79
-rw-r--r--lib/activate/activate.h10
-rw-r--r--lib/activate/dev_manager.c16
-rw-r--r--lib/commands/toolcontext.c3
-rw-r--r--lib/format_text/flags.c3
-rw-r--r--lib/integrity/integrity.c329
-rw-r--r--lib/label/label.c2
-rw-r--r--lib/label/label.h8
-rw-r--r--lib/metadata/integrity_manip.c649
-rw-r--r--lib/metadata/lv.c7
-rw-r--r--lib/metadata/lv_manip.c225
-rw-r--r--lib/metadata/merge.c2
-rw-r--r--lib/metadata/metadata-exported.h31
-rw-r--r--lib/metadata/segtype.h6
-rw-r--r--lib/raid/raid.c36
-rw-r--r--tools/args.h9
-rw-r--r--tools/command-lines.in25
-rw-r--r--tools/lv_types.h1
-rw-r--r--tools/lvconvert.c69
-rw-r--r--tools/lvcreate.c38
-rw-r--r--tools/lvmcmdline.c3
-rw-r--r--tools/toollib.c172
-rw-r--r--tools/tools.h5
27 files changed, 1871 insertions, 57 deletions
diff --git a/device_mapper/all.h b/device_mapper/all.h
index 57673b44a..61b7493f8 100644
--- a/device_mapper/all.h
+++ b/device_mapper/all.h
@@ -392,6 +392,15 @@ struct dm_status_writecache {
int dm_get_status_writecache(struct dm_pool *mem, const char *params,
struct dm_status_writecache **status);
+struct dm_status_integrity {
+ uint64_t number_of_mismatches;
+ uint64_t provided_data_sectors;
+ uint64_t recalc_sector;
+};
+
+int dm_get_status_integrity(struct dm_pool *mem, const char *params,
+ struct dm_status_integrity **status);
+
/*
* Parse params from STATUS call for snapshot target
*
@@ -970,6 +979,36 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
uint32_t writecache_block_size,
struct writecache_settings *settings);
+struct integrity_settings {
+ char mode[8];
+ uint32_t tag_size;
+ const char *internal_hash;
+
+ uint32_t journal_sectors;
+ uint32_t interleave_sectors;
+ uint32_t buffer_sectors;
+ uint32_t journal_watermark;
+ uint32_t commit_time;
+ uint32_t block_size;
+ uint32_t bitmap_flush_interval;
+ uint64_t sectors_per_bit;
+
+ unsigned journal_sectors_set:1;
+ unsigned interleave_sectors_set:1;
+ unsigned buffer_sectors_set:1;
+ unsigned journal_watermark_set:1;
+ unsigned commit_time_set:1;
+ unsigned block_size_set:1;
+ unsigned bitmap_flush_interval_set:1;
+ unsigned sectors_per_bit_set:1;
+};
+
+int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *meta_uuid,
+ struct integrity_settings *settings,
+ int recalculate);
/*
* VDO target
diff --git a/device_mapper/libdm-deptree.c b/device_mapper/libdm-deptree.c
index 7fac6ab20..8cde5ff58 100644
--- a/device_mapper/libdm-deptree.c
+++ b/device_mapper/libdm-deptree.c
@@ -38,6 +38,7 @@ enum {
SEG_STRIPED,
SEG_ZERO,
SEG_WRITECACHE,
+ SEG_INTEGRITY,
SEG_THIN_POOL,
SEG_THIN,
SEG_VDO,
@@ -78,6 +79,7 @@ static const struct {
{ SEG_STRIPED, "striped" },
{ SEG_ZERO, "zero"},
{ SEG_WRITECACHE, "writecache"},
+ { SEG_INTEGRITY, "integrity"},
{ SEG_THIN_POOL, "thin-pool"},
{ SEG_THIN, "thin"},
{ SEG_VDO, "vdo" },
@@ -221,6 +223,11 @@ struct load_segment {
int writecache_pmem; /* writecache, 1 if pmem, 0 if ssd */
uint32_t writecache_block_size; /* writecache, in bytes */
struct writecache_settings writecache_settings; /* writecache */
+
+ uint64_t integrity_data_sectors; /* integrity (provided_data_sectors) */
+ struct dm_tree_node *integrity_meta_node; /* integrity */
+ struct integrity_settings integrity_settings; /* integrity */
+ int integrity_recalculate; /* integrity */
};
/* Per-device properties */
@@ -2705,6 +2712,88 @@ static int _writecache_emit_segment_line(struct dm_task *dmt,
return 1;
}
+static int _integrity_emit_segment_line(struct dm_task *dmt,
+ struct load_segment *seg,
+ char *params, size_t paramsize)
+{
+ struct integrity_settings *set = &seg->integrity_settings;
+ int pos = 0;
+ int count;
+ char origin_dev[DM_FORMAT_DEV_BUFSIZE];
+ char meta_dev[DM_FORMAT_DEV_BUFSIZE];
+
+ if (!_build_dev_string(origin_dev, sizeof(origin_dev), seg->origin))
+ return_0;
+
+ if (seg->integrity_meta_node &&
+ !_build_dev_string(meta_dev, sizeof(meta_dev), seg->integrity_meta_node))
+ return_0;
+
+ count = 1; /* for internal_hash which we always pass in */
+
+ if (seg->integrity_meta_node)
+ count++;
+
+ if (seg->integrity_recalculate)
+ count++;
+
+ if (set->journal_sectors_set)
+ count++;
+ if (set->interleave_sectors_set)
+ count++;
+ if (set->buffer_sectors_set)
+ count++;
+ if (set->journal_watermark_set)
+ count++;
+ if (set->commit_time_set)
+ count++;
+ if (set->block_size_set)
+ count++;
+ if (set->bitmap_flush_interval_set)
+ count++;
+ if (set->sectors_per_bit_set)
+ count++;
+
+ EMIT_PARAMS(pos, "%s 0 %u %s %d internal_hash:%s",
+ origin_dev,
+ set->tag_size,
+ set->mode,
+ count,
+ set->internal_hash);
+
+ if (seg->integrity_meta_node)
+ EMIT_PARAMS(pos, " meta_device:%s", meta_dev);
+
+ if (seg->integrity_recalculate)
+ EMIT_PARAMS(pos, " recalculate");
+
+ if (set->journal_sectors_set)
+ EMIT_PARAMS(pos, " journal_sectors:%u", set->journal_sectors);
+
+ if (set->interleave_sectors_set)
+ EMIT_PARAMS(pos, " ineterleave_sectors:%u", set->interleave_sectors);
+
+ if (set->buffer_sectors_set)
+ EMIT_PARAMS(pos, " buffer_sectors:%u", set->buffer_sectors);
+
+ if (set->journal_watermark_set)
+ EMIT_PARAMS(pos, " journal_watermark:%u", set->journal_watermark);
+
+ if (set->commit_time_set)
+ EMIT_PARAMS(pos, " commit_time:%u", set->commit_time);
+
+ if (set->block_size_set)
+ EMIT_PARAMS(pos, " block_size:%u", set->block_size);
+
+ if (set->bitmap_flush_interval_set)
+ EMIT_PARAMS(pos, " bitmap_flush_interval:%u", set->bitmap_flush_interval);
+
+ if (set->sectors_per_bit_set)
+ EMIT_PARAMS(pos, " sectors_per_bit:%llu", (unsigned long long)set->sectors_per_bit);
+
+ return 1;
+}
+
static int _thin_pool_emit_segment_line(struct dm_task *dmt,
struct load_segment *seg,
char *params, size_t paramsize)
@@ -2889,6 +2978,10 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
if (!_writecache_emit_segment_line(dmt, seg, params, paramsize))
return_0;
break;
+ case SEG_INTEGRITY:
+ if (!_integrity_emit_segment_line(dmt, seg, params, paramsize))
+ return_0;
+ break;
}
switch(seg->type) {
@@ -2901,6 +2994,7 @@ static int _emit_segment_line(struct dm_task *dmt, uint32_t major,
case SEG_THIN:
case SEG_CACHE:
case SEG_WRITECACHE:
+ case SEG_INTEGRITY:
break;
case SEG_CRYPT:
case SEG_LINEAR:
@@ -3738,6 +3832,42 @@ int dm_tree_node_add_writecache_target(struct dm_tree_node *node,
return 1;
}
+int dm_tree_node_add_integrity_target(struct dm_tree_node *node,
+ uint64_t size,
+ const char *origin_uuid,
+ const char *meta_uuid,
+ struct integrity_settings *settings,
+ int recalculate)
+{
+ struct load_segment *seg;
+
+ if (!(seg = _add_segment(node, SEG_INTEGRITY, size)))
+ return_0;
+
+ if (meta_uuid) {
+ if (!(seg->integrity_meta_node = dm_tree_find_node_by_uuid(node->dtree, meta_uuid))) {
+ log_error("Missing integrity's meta uuid %s.", meta_uuid);
+ return 0;
+ }
+
+ if (!_link_tree_nodes(node, seg->integrity_meta_node))
+ return_0;
+ }
+
+ if (!(seg->origin = dm_tree_find_node_by_uuid(node->dtree, origin_uuid))) {
+ log_error("Missing integrity's origin uuid %s.", origin_uuid);
+ return 0;
+ }
+ if (!_link_tree_nodes(node, seg->origin))
+ return_0;
+
+ memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
+
+ seg->integrity_recalculate = recalculate;
+
+ return 1;
+}
+
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
uint64_t size,
const char *rlog_uuid,
diff --git a/device_mapper/libdm-targets.c b/device_mapper/libdm-targets.c
index d82e28b13..4289927bd 100644
--- a/device_mapper/libdm-targets.c
+++ b/device_mapper/libdm-targets.c
@@ -380,6 +380,35 @@ int dm_get_status_writecache(struct dm_pool *mem, const char *params,
return 1;
}
+int dm_get_status_integrity(struct dm_pool *mem, const char *params,
+ struct dm_status_integrity **status)
+{
+ struct dm_status_integrity *s;
+ char recalc_str[8];
+
+ if (!(s = dm_pool_zalloc(mem, sizeof(struct dm_status_integrity))))
+ return_0;
+
+ memset(recalc_str, 0, sizeof(recalc_str));
+
+ if (sscanf(params, "%llu %llu %s",
+ (unsigned long long *)&s->number_of_mismatches,
+ (unsigned long long *)&s->provided_data_sectors,
+ recalc_str) != 3) {
+ log_error("Failed to parse integrity params: %s.", params);
+ dm_pool_free(mem, s);
+ return 0;
+ }
+
+ if (recalc_str[0] == '-')
+ s->recalc_sector = 0;
+ else
+ s->recalc_sector = strtoull(recalc_str, NULL, 0);
+
+ *status = s;
+ return 1;
+}
+
int parse_thin_pool_status(const char *params, struct dm_status_thin_pool *s)
{
int pos;
diff --git a/lib/Makefile.in b/lib/Makefile.in
index c037b4162..86ced01ab 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -20,6 +20,7 @@ SOURCES =\
activate/activate.c \
cache/lvmcache.c \
writecache/writecache.c \
+ integrity/integrity.c \
cache_segtype/cache.c \
commands/toolcontext.c \
config/config.c \
@@ -67,6 +68,7 @@ SOURCES =\
log/log.c \
metadata/cache_manip.c \
metadata/writecache_manip.c \
+ metadata/integrity_manip.c \
metadata/lv.c \
metadata/lv_manip.c \
metadata/merge.c \
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
index a82a5cbc4..b4c39d544 100644
--- a/lib/activate/activate.c
+++ b/lib/activate/activate.c
@@ -325,25 +325,11 @@ int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s, unsigned or
{
return 1;
}
-int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
-{
- return 1;
-}
int lv_activation_filter(struct cmd_context *cmd, const char *lvid_s,
int *activate_lv, const struct logical_volume *lv)
{
return 1;
}
-int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive, int noscan,
- int temporary, const struct logical_volume *lv)
-{
- return 1;
-}
-int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
- int noscan, int temporary, const struct logical_volume *lv)
-{
- return 1;
-}
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
{
return 1;
@@ -2413,7 +2399,7 @@ static int _lv_has_open_snapshots(const struct logical_volume *lv)
return r;
}
-int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
+static int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv)
{
struct lvinfo info;
static const struct lv_activate_opts laopts = { .skip_in_use = 1 };
@@ -2609,34 +2595,6 @@ out:
return r;
}
-/* Activate LV */
-int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive,
- int noscan, int temporary, const struct logical_volume *lv)
-{
- struct lv_activate_opts laopts = { .exclusive = exclusive,
- .noscan = noscan,
- .temporary = temporary };
-
- if (!_lv_activate(cmd, lvid_s, &laopts, 0, lv))
- return_0;
-
- return 1;
-}
-
-/* Activate LV only if it passes filter */
-int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
- int noscan, int temporary, const struct logical_volume *lv)
-{
- struct lv_activate_opts laopts = { .exclusive = exclusive,
- .noscan = noscan,
- .temporary = temporary };
-
- if (!_lv_activate(cmd, lvid_s, &laopts, 1, lv))
- return_0;
-
- return 1;
-}
-
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv)
{
int r;
@@ -2867,11 +2825,19 @@ int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
return 1;
}
-int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
+int activate_lv_opts(struct cmd_context *cmd, const struct logical_volume *lv,
+ struct lv_activate_opts *laopts)
{
const struct logical_volume *active_lv;
+ const struct logical_volume *lv_use;
int ret;
+ if (lv->status & LV_NOSCAN)
+ laopts->noscan = 1;
+
+ if (lv->status & LV_TEMPORARY)
+ laopts->temporary = 1;
+
/*
* When trying activating component LV, make sure none of sub component
* LV or LVs that are using it are active.
@@ -2888,19 +2854,34 @@ int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
goto out;
}
- ret = lv_activate_with_filter(cmd, NULL, 0,
- (lv->status & LV_NOSCAN) ? 1 : 0,
- (lv->status & LV_TEMPORARY) ? 1 : 0,
- lv_committed(lv));
+ if (lv->status & LV_UNCOMMITTED)
+ lv_use = lv;
+ else
+ lv_use = lv_committed(lv);
+
+ ret = _lv_activate(cmd, NULL, laopts, 1, lv_use);
out:
return ret;
}
+int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
+{
+ struct lv_activate_opts laopts = { 0 };
+
+ return activate_lv_opts(cmd, lv, &laopts);
+}
+
int deactivate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
{
+ const struct logical_volume *lv_use;
int ret;
- ret = lv_deactivate(cmd, NULL, lv_committed(lv));
+ if (lv->status & LV_UNCOMMITTED)
+ lv_use = lv;
+ else
+ lv_use = lv_committed(lv);
+
+ ret = lv_deactivate(cmd, NULL, lv_use);
return ret;
}
diff --git a/lib/activate/activate.h b/lib/activate/activate.h
index a5ee438ad..221510b78 100644
--- a/lib/activate/activate.h
+++ b/lib/activate/activate.h
@@ -39,6 +39,7 @@ typedef enum {
SEG_STATUS_THIN_POOL,
SEG_STATUS_VDO_POOL,
SEG_STATUS_WRITECACHE,
+ SEG_STATUS_INTEGRITY,
SEG_STATUS_UNKNOWN
} lv_seg_status_type_t;
@@ -53,6 +54,7 @@ struct lv_seg_status {
struct dm_status_thin *thin;
struct dm_status_thin_pool *thin_pool;
struct dm_status_writecache *writecache;
+ struct dm_status_integrity *integrity;
struct lv_status_vdo vdo_pool;
};
};
@@ -72,6 +74,7 @@ struct lv_activate_opts {
int no_merging;
int send_messages;
int skip_in_use;
+ int integrity_recalculate;
unsigned revert;
unsigned read_only;
unsigned noscan; /* Mark this LV to avoid its scanning. This also
@@ -122,9 +125,6 @@ int lv_resume_if_active(struct cmd_context *cmd, const char *lvid_s,
unsigned origin_only, unsigned exclusive, unsigned revert, const struct logical_volume *lv);
int lv_activate(struct cmd_context *cmd, const char *lvid_s, int exclusive,
int noscan, int temporary, const struct logical_volume *lv);
-int lv_activate_with_filter(struct cmd_context *cmd, const char *lvid_s, int exclusive,
- int noscan, int temporary, const struct logical_volume *lv);
-int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logical_volume *lv);
int lv_mknodes(struct cmd_context *cmd, const struct logical_volume *lv);
@@ -132,6 +132,8 @@ int lv_deactivate_any_missing_subdevs(const struct logical_volume *lv);
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv);
int deactivate_lv(struct cmd_context *cmd, const struct logical_volume *lv);
+int activate_lv_opts(struct cmd_context *cmd, const struct logical_volume *lv,
+ struct lv_activate_opts *laopts);
int suspend_lv(struct cmd_context *cmd, const struct logical_volume *lv);
int suspend_lv_origin(struct cmd_context *cmd, const struct logical_volume *lv);
int resume_lv(struct cmd_context *cmd, const struct logical_volume *lv);
@@ -260,6 +262,7 @@ void fs_unlock(void);
#define TARGET_NAME_CACHE "cache"
#define TARGET_NAME_WRITECACHE "writecache"
+#define TARGET_NAME_INTEGRITY "integrity"
#define TARGET_NAME_ERROR "error"
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
#define TARGET_NAME_LINEAR "linear"
@@ -277,6 +280,7 @@ void fs_unlock(void);
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
#define MODULE_NAME_WRITECACHE TARGET_NAME_WRITECACHE
+#define MODULE_NAME_INTEGRITY TARGET_NAME_INTEGRITY
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
#define MODULE_NAME_LOG_USERSPACE "log-userspace"
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index 8569e860b..b31cfa0ab 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -222,6 +222,10 @@ static int _get_segment_status_from_target_params(const char *target_name,
if (!dm_get_status_writecache(seg_status->mem, params, &(seg_status->writecache)))
return_0;
seg_status->type = SEG_STATUS_WRITECACHE;
+ } else if (segtype_is_integrity(segtype)) {
+ if (!dm_get_status_integrity(seg_status->mem, params, &(seg_status->integrity)))
+ return_0;
+ seg_status->type = SEG_STATUS_INTEGRITY;
} else
/*
* TODO: Add support for other segment types too!
@@ -299,6 +303,9 @@ static int _info_run(const char *dlid, struct dm_info *dminfo,
if (lv_is_vdo_pool(seg_status->seg->lv))
length = get_vdo_pool_virtual_size(seg_status->seg);
+ if (lv_is_integrity(seg_status->seg->lv))
+ length = seg_status->seg->integrity_data_sectors;
+
do {
target = dm_get_next_target(dmt, target, &target_start,
&target_length, &target_name, &target_params);
@@ -2620,6 +2627,10 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
if (!_add_lv_to_dtree(dm, dtree, seg->writecache, dm->activation ? origin_only : 1))
return_0;
}
+ if (seg->integrity_meta_dev && seg_is_integrity(seg)) {
+ if (!_add_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, dm->activation ? origin_only : 1))
+ return_0;
+ }
if (seg->pool_lv &&
(lv_is_cache_pool(seg->pool_lv) || lv_is_cache_vol(seg->pool_lv) || dm->track_external_lv_deps) &&
/* When activating and not origin_only detect linear 'overlay' over pool */
@@ -3076,6 +3087,11 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
lv_layer(seg->writecache)))
return_0;
+ if (seg->integrity_meta_dev && !laopts->origin_only &&
+ !_add_new_lv_to_dtree(dm, dtree, seg->integrity_meta_dev, laopts,
+ lv_layer(seg->integrity_meta_dev)))
+ return_0;
+
/* Add any LVs used by this segment */
for (s = 0; s < seg->area_count; ++s) {
if ((seg_type(seg, s) == AREA_LV) &&
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 479d4991c..7f8e346c6 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -1362,6 +1362,9 @@ static int _init_segtypes(struct cmd_context *cmd)
return 0;
#endif
+ if (!init_integrity_segtypes(cmd, &seglib))
+ return 0;
+
return 1;
}
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
index 2873ba632..a86732865 100644
--- a/lib/format_text/flags.c
+++ b/lib/format_text/flags.c
@@ -104,8 +104,11 @@ static const struct flag _lv_flags[] = {
{LV_VDO_POOL, NULL, 0},
{LV_VDO_POOL_DATA, NULL, 0},
{WRITECACHE, NULL, 0},
+ {INTEGRITY, NULL, 0},
+ {INTEGRITY_METADATA, NULL, 0},
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
{LV_REMOVED, NULL, 0},
+ {LV_UNCOMMITTED, NULL, 0},
{0, NULL, 0}
};
diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c
new file mode 100644
index 000000000..f3e465bd1
--- /dev/null
+++ b/lib/integrity/integrity.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "base/memory/zalloc.h"
+#include "lib/misc/lib.h"
+#include "lib/commands/toolcontext.h"
+#include "lib/metadata/segtype.h"
+#include "lib/display/display.h"
+#include "lib/format_text/text_export.h"
+#include "lib/config/config.h"
+#include "lib/datastruct/str_list.h"
+#include "lib/misc/lvm-string.h"
+#include "lib/activate/activate.h"
+#include "lib/metadata/metadata.h"
+#include "lib/metadata/lv_alloc.h"
+#include "lib/config/defaults.h"
+
+#define SEG_LOG_ERROR(t, p...) \
+ log_error(t " segment %s of logical volume %s.", ## p, \
+ dm_config_parent_name(sn), seg->lv->name), 0;
+
+static void _integrity_display(const struct lv_segment *seg)
+{
+ /* TODO: lvdisplay segments */
+}
+
+static int _integrity_text_import(struct lv_segment *seg,
+ const struct dm_config_node *sn,
+ struct dm_hash_table *pv_hash __attribute__((unused)))
+{
+ struct integrity_settings *set;
+ struct logical_volume *origin_lv = NULL;
+ struct logical_volume *meta_lv = NULL;
+ const char *origin_name = NULL;
+ const char *meta_dev = NULL;
+ const char *mode = NULL;
+ const char *hash = NULL;
+
+ memset(&seg->integrity_settings, 0, sizeof(struct integrity_settings));
+ set = &seg->integrity_settings;
+
+ /* origin always set */
+
+ if (!dm_config_has_node(sn, "origin"))
+ return SEG_LOG_ERROR("origin not specified in");
+
+ if (!dm_config_get_str(sn, "origin", &origin_name))
+ return SEG_LOG_ERROR("origin must be a string in");
+
+ if (!(origin_lv = find_lv(seg->lv->vg, origin_name)))
+ return SEG_LOG_ERROR("Unknown LV specified for integrity origin %s in", origin_name);
+
+ if (!set_lv_segment_area_lv(seg, 0, origin_lv, 0, 0))
+ return_0;
+
+ /* data_sectors always set */
+
+ if (!dm_config_get_uint64(sn, "data_sectors", &seg->integrity_data_sectors))
+ return SEG_LOG_ERROR("integrity data_sectors must be set in");
+
+ /* mode always set */
+
+ if (!dm_config_get_str(sn, "mode", &mode))
+ return SEG_LOG_ERROR("integrity mode must be set in");
+
+ if (strlen(mode) > 7)
+ return SEG_LOG_ERROR("integrity mode invalid in");
+
+ strncpy(set->mode, mode, 7);
+
+ /* tag_size always set */
+
+ if (!dm_config_get_uint32(sn, "tag_size", &set->tag_size))
+ return SEG_LOG_ERROR("integrity tag_size must be set in");
+
+ /* internal_hash always set */
+
+ if (!dm_config_get_str(sn, "internal_hash", &hash))
+ return SEG_LOG_ERROR("integrity internal_hash must be set in");
+
+ if (!(set->internal_hash = strdup(hash)))
+ return SEG_LOG_ERROR("integrity internal_hash failed to be set in");
+
+ /* meta_dev optional */
+
+ if (dm_config_has_node(sn, "meta_dev")) {
+ if (!dm_config_get_str(sn, "meta_dev", &meta_dev))
+ return SEG_LOG_ERROR("meta_dev must be a string in");
+
+ if (!(meta_lv = find_lv(seg->lv->vg, meta_dev)))
+ return SEG_LOG_ERROR("Unknown logical volume %s specified for integrity in", meta_dev);
+ }
+
+ /* the rest are optional */
+
+ if (dm_config_has_node(sn, "journal_sectors")) {
+ if (!dm_config_get_uint32(sn, "journal_sectors", &set->journal_sectors))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->journal_sectors_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "interleave_sectors")) {
+ if (!dm_config_get_uint32(sn, "interleave_sectors", &set->interleave_sectors))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->interleave_sectors_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "buffer_sectors")) {
+ if (!dm_config_get_uint32(sn, "buffer_sectors", &set->buffer_sectors))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->buffer_sectors_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "journal_watermark")) {
+ if (!dm_config_get_uint32(sn, "journal_watermark", &set->journal_watermark))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->journal_watermark_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "commit_time")) {
+ if (!dm_config_get_uint32(sn, "commit_time", &set->commit_time))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->commit_time_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "block_size")) {
+ if (!dm_config_get_uint32(sn, "block_size", &set->block_size))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->block_size_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "bitmap_flush_interval")) {
+ if (!dm_config_get_uint32(sn, "bitmap_flush_interval", &set->bitmap_flush_interval))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->bitmap_flush_interval_set = 1;
+ }
+
+ if (dm_config_has_node(sn, "sectors_per_bit")) {
+ if (!dm_config_get_uint64(sn, "sectors_per_bit", &set->sectors_per_bit))
+ return SEG_LOG_ERROR("Unknown integrity_setting in");
+ set->sectors_per_bit_set = 1;
+ }
+
+ seg->origin = origin_lv;
+ seg->integrity_meta_dev = meta_lv;
+ seg->lv->status |= INTEGRITY;
+
+ if (meta_lv)
+ meta_lv->status |= INTEGRITY_METADATA;
+
+ if (meta_lv && !add_seg_to_segs_using_this_lv(meta_lv, seg))
+ return_0;
+
+ return 1;
+}
+
+static int _integrity_text_import_area_count(const struct dm_config_node *sn,
+ uint32_t *area_count)
+{
+ *area_count = 1;
+
+ return 1;
+}
+
+static int _integrity_text_export(const struct lv_segment *seg,
+ struct formatter *f)
+{
+ const struct integrity_settings *set = &seg->integrity_settings;
+
+ outf(f, "origin = \"%s\"", seg_lv(seg, 0)->name);
+ outf(f, "data_sectors = %llu", (unsigned long long)seg->integrity_data_sectors);
+
+ outf(f, "mode = \"%s\"", set->mode);
+ outf(f, "tag_size = %u", set->tag_size);
+ outf(f, "internal_hash = \"%s\"", set->internal_hash);
+
+ if (seg->integrity_meta_dev)
+ outf(f, "meta_dev = \"%s\"", seg->integrity_meta_dev->name);
+
+ if (set->journal_sectors_set)
+ outf(f, "journal_sectors = %u", set->journal_sectors);
+
+ if (set->interleave_sectors_set)
+ outf(f, "interleave_sectors = %u", set->interleave_sectors);
+
+ if (set->buffer_sectors_set)
+ outf(f, "buffer_sectors = %u", set->buffer_sectors);
+
+ if (set->journal_watermark_set)
+ outf(f, "journal_watermark = %u", set->journal_watermark);
+
+ if (set->commit_time_set)
+ outf(f, "commit_time = %u", set->commit_time);
+
+ if (set->block_size_set)
+ outf(f, "block_size = %u", set->block_size);
+
+ if (set->bitmap_flush_interval)
+ outf(f, "bitmap_flush_interval = %u", set->bitmap_flush_interval);
+
+ if (set->sectors_per_bit)
+ outf(f, "sectors_per_bit = %llu", (unsigned long long)set->sectors_per_bit);
+
+ return 1;
+}
+
+static void _destroy(struct segment_type *segtype)
+{
+ free((void *) segtype);
+}
+
+#ifdef DEVMAPPER_SUPPORT
+
+static int _target_present(struct cmd_context *cmd,
+ const struct lv_segment *seg __attribute__((unused)),
+ unsigned *attributes __attribute__((unused)))
+{
+ static int _integrity_checked = 0;
+ static int _integrity_present = 0;
+
+ if (!activation())
+ return 0;
+
+ if (!_integrity_checked) {
+ _integrity_checked = 1;
+ _integrity_present = target_present(cmd, TARGET_NAME_INTEGRITY, 0);
+ }
+
+ return _integrity_present;
+}
+
+static int _modules_needed(struct dm_pool *mem,
+ const struct lv_segment *seg __attribute__((unused)),
+ struct dm_list *modules)
+{
+ if (!str_list_add(mem, modules, MODULE_NAME_INTEGRITY)) {
+ log_error("String list allocation failed for integrity module.");
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* DEVMAPPER_SUPPORT */
+
+#ifdef DEVMAPPER_SUPPORT
+static int _integrity_add_target_line(struct dev_manager *dm,
+ struct dm_pool *mem,
+ struct cmd_context *cmd __attribute__((unused)),
+ void **target_state __attribute__((unused)),
+ struct lv_segment *seg,
+ const struct lv_activate_opts *laopts,
+ struct dm_tree_node *node, uint64_t len,
+ uint32_t *pvmove_mirror_count __attribute__((unused)))
+{
+ char *origin_uuid;
+ char *meta_uuid = NULL;
+
+ if (!seg_is_integrity(seg)) {
+ log_error(INTERNAL_ERROR "Passed segment is not integrity.");
+ return 0;
+ }
+
+ if (!(origin_uuid = build_dm_uuid(mem, seg_lv(seg, 0), NULL)))
+ return_0;
+
+ if (seg->integrity_meta_dev) {
+ if (!(meta_uuid = build_dm_uuid(mem, seg->integrity_meta_dev, NULL)))
+ return_0;
+ }
+
+ if (!seg->integrity_data_sectors) {
+ log_error("_integrity_add_target_line zero size");
+ return_0;
+ }
+
+ if (!dm_tree_node_add_integrity_target(node, seg->integrity_data_sectors,
+ origin_uuid, meta_uuid,
+ &seg->integrity_settings,
+ laopts->integrity_recalculate))
+ return_0;
+
+ return 1;
+}
+#endif /* DEVMAPPER_SUPPORT */
+
+static struct segtype_handler _integrity_ops = {
+ .display = _integrity_display,
+ .text_import = _integrity_text_import,
+ .text_import_area_count = _integrity_text_import_area_count,
+ .text_export = _integrity_text_export,
+#ifdef DEVMAPPER_SUPPORT
+ .add_target_line = _integrity_add_target_line,
+ .target_present = _target_present,
+ .modules_needed = _modules_needed,
+#endif
+ .destroy = _destroy,
+};
+
+int init_integrity_segtypes(struct cmd_context *cmd,
+ struct segtype_library *seglib)
+{
+ struct segment_type *segtype = zalloc(sizeof(*segtype));
+
+ if (!segtype) {
+ log_error("Failed to allocate memory for integrity segtype");
+ return 0;
+ }
+
+ segtype->name = SEG_TYPE_NAME_INTEGRITY;
+ segtype->flags = SEG_INTEGRITY;
+ segtype->ops = &_integrity_ops;
+
+ if (!lvm_register_segtype(seglib, segtype))
+ return_0;
+ log_very_verbose("Initialised segtype: %s", segtype->name);
+
+ return 1;
+}
diff --git a/lib/label/label.c b/lib/label/label.c
index 680630b8e..82814faa9 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -254,8 +254,6 @@ struct label *label_create(struct labeller *labeller)
/* global variable for accessing the bcache populated by label scan */
struct bcache *scan_bcache;
-#define BCACHE_BLOCK_SIZE_IN_SECTORS 256 /* 256*512 = 128K */
-
static bool _in_bcache(struct device *dev)
{
if (!dev)
diff --git a/lib/label/label.h b/lib/label/label.h
index 410890621..06d0f29a1 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -121,6 +121,14 @@ int label_scan_open_rw(struct device *dev);
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
/*
+ * These are the sizes the label.c uses to set up
+ * and use bcache (they are not bcache restrictions
+ * or defs.)
+ */
+#define BCACHE_BLOCK_SIZE_IN_SECTORS 256 /* 256*512 = 128K */
+#define BCACHE_BLOCK_SIZE_IN_BYTES 131072
+
+/*
* Wrappers around bcache equivalents.
* (these make it easier to disable bcache and revert to direct rw if needed)
*/
diff --git a/lib/metadata/integrity_manip.c b/lib/metadata/integrity_manip.c
new file mode 100644
index 000000000..ca3094ffb
--- /dev/null
+++ b/lib/metadata/integrity_manip.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2014-2015 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of LVM2.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU Lesser General Public License v.2.1.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/misc/lib.h"
+#include "lib/metadata/metadata.h"
+#include "lib/locking/locking.h"
+#include "lib/misc/lvm-string.h"
+#include "lib/commands/toolcontext.h"
+#include "lib/display/display.h"
+#include "lib/metadata/segtype.h"
+#include "lib/activate/activate.h"
+#include "lib/config/defaults.h"
+#include "lib/activate/dev_manager.h"
+
+#define DEFAULT_TAG_SIZE 4 /* bytes */
+#define DEFAULT_MODE 'B'
+#define DEFAULT_INTERNAL_HASH "crc32c"
+
+#define ONE_MB_IN_BYTES 1048576
+
+int lv_is_integrity_origin(const struct logical_volume *lv)
+{
+ struct seg_list *sl;
+
+ dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
+ if (!sl->seg || !sl->seg->lv || !sl->seg->origin)
+ continue;
+ if (lv_is_integrity(sl->seg->lv) && (sl->seg->origin == lv))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Every 500M of data needs 4M of metadata.
+ * (From trial and error testing.)
+ */
+static uint64_t _lv_size_bytes_to_integrity_meta_bytes(uint64_t lv_size_bytes)
+{
+ return ((lv_size_bytes / (500 * ONE_MB_IN_BYTES)) + 1) * (4 * ONE_MB_IN_BYTES);
+}
+
+/*
+ * The user wants external metadata, but did not specify an existing
+ * LV to hold metadata, so create an LV for metadata. Save it in
+ * lp->integrity_meta_lv and it will be passed to lv_add_integrity().
+ */
+int lv_create_integrity_metadata(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct lvcreate_params *lp)
+{
+ char metaname[NAME_LEN];
+ uint64_t lv_size_bytes, meta_bytes, meta_sectors;
+ struct logical_volume *lv;
+ struct lvcreate_params lp_meta = {
+ .activate = CHANGE_AN,
+ .alloc = ALLOC_INHERIT,
+ .major = -1,
+ .minor = -1,
+ .permission = LVM_READ | LVM_WRITE,
+ .pvh = &vg->pvs,
+ .read_ahead = DM_READ_AHEAD_NONE,
+ .stripes = 1,
+ .vg_name = vg->name,
+ .zero = 0,
+ .wipe_signatures = 0,
+ .suppress_zero_warn = 1,
+ };
+
+ if (dm_snprintf(metaname, NAME_LEN, "%s_imeta", lp->lv_name) < 0) {
+ log_error("Failed to create metadata LV name.");
+ return 0;
+ }
+
+ if (!(lp_meta.lv_name = strdup(metaname)))
+ return_0;
+
+ lp_meta.pvh = lp->pvh;
+
+ lv_size_bytes = lp->extents * vg->extent_size * 512;
+ meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv_size_bytes);
+ meta_sectors = meta_bytes / 512;
+ lp_meta.extents = meta_sectors / vg->extent_size;
+
+ log_print("Creating integrity metadata LV %s with size %s.",
+ metaname, display_size(cmd, meta_sectors));
+
+ dm_list_init(&lp_meta.tags);
+
+ if (!(lp_meta.segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
+ return_0;
+
+ if (!(lv = lv_create_single(vg, &lp_meta))) {
+ log_error("Failed to create integrity metadata LV");
+ return 0;
+ }
+
+ lp->integrity_meta_lv = lv;
+ return 1;
+}
+
+static int _get_provided_data_sectors(struct logical_volume *lv, uint64_t *provided_data_sectors)
+{
+ struct lv_with_info_and_seg_status status;
+ uint64_t data_sectors, extra_sectors;
+
+ memset(&status, 0, sizeof(status));
+ status.seg_status.type = SEG_STATUS_NONE;
+
+ status.seg_status.seg = first_seg(lv);
+
+ /* FIXME: why reporter_pool? */
+ if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024))) {
+ log_error("Failed to get mem for LV status.");
+ return 0;
+ }
+
+ if (!lv_info_with_seg_status(lv->vg->cmd, first_seg(lv), &status, 1, 1)) {
+ log_error("Failed to get device mapper status for %s", display_lvname(lv));
+ goto fail;
+ }
+
+ if (!status.info.exists) {
+ log_error("No device mapper info exists for %s", display_lvname(lv));
+ goto fail;
+ }
+
+ if (status.seg_status.type != SEG_STATUS_INTEGRITY) {
+ log_error("Invalid device mapper status type (%d) for %s",
+ (uint32_t)status.seg_status.type, display_lvname(lv));
+ goto fail;
+ }
+
+ data_sectors = status.seg_status.integrity->provided_data_sectors;
+
+ if ((extra_sectors = (data_sectors % lv->vg->extent_size))) {
+ data_sectors -= extra_sectors;
+ log_debug("Reduce provided_data_sectors by %llu to %llu for extent alignment",
+ (unsigned long long)extra_sectors, (unsigned long long)data_sectors);
+ }
+
+ *provided_data_sectors = data_sectors;
+
+ dm_pool_destroy(status.seg_status.mem);
+ return 1;
+
+fail:
+ dm_pool_destroy(status.seg_status.mem);
+ return 0;
+}
+
+int lv_remove_integrity(struct logical_volume *lv)
+{
+ struct lv_segment *seg = first_seg(lv);
+ struct logical_volume *origin;
+ struct logical_volume *meta_lv;
+
+ /* FIXME: handle removing integrity from raid images. */
+
+ if (!seg_is_integrity(seg)) {
+ log_error("LV %s segment is not integrity.", display_lvname(lv));
+ return 0;
+ }
+
+ if (!(meta_lv = seg->integrity_meta_dev)) {
+ log_error("LV %s segment has no integrity metadata device.", display_lvname(lv));
+ return 0;
+ }
+
+ if (!(origin = seg_lv(seg, 0))) {
+ log_error("LV %s integrity segment has no origin", display_lvname(lv));
+ return 0;
+ }
+
+ if (!remove_seg_from_segs_using_this_lv(seg->integrity_meta_dev, seg))
+ return_0;
+
+ lv_set_visible(seg->integrity_meta_dev);
+
+ lv->status &= ~INTEGRITY;
+ meta_lv->status &= ~INTEGRITY_METADATA;
+
+ seg->integrity_meta_dev = NULL;
+
+ if (!remove_layer_from_lv(lv, origin))
+ return_0;
+
+ if (!lv_remove(origin))
+ return_0;
+
+ if (!lv_remove(meta_lv))
+ log_warn("WARNING: failed to remove integrity metadata LV.");
+
+ return 1;
+}
+
+/*
+ * Add integrity to each raid image.
+ *
+ * for each rimage_N:
+ * . create and allocate a new linear LV rimage_N_imeta
+ * . move the segments from rimage_N to a new rimage_N_iorig
+ * . add an integrity segment to rimage_N with
+ * origin=rimage_N_iorig, meta_dev=rimage_N_imeta
+ *
+ * Before:
+ * rimage_0
+ * segment1: striped: pv0:A
+ * rimage_1
+ * segment1: striped: pv1:B
+ *
+ * After:
+ * rimage_0
+ * segment1: integrity: rimage_0_iorig, rimage_0_imeta
+ * rimage_1
+ * segment1: integrity: rimage_1_iorig, rimage_1_imeta
+ * rimage_0_iorig
+ * segment1: striped: pv0:A
+ * rimage_1_iorig
+ * segment1: striped: pv1:B
+ * rimage_0_imeta
+ * segment1: striped: pv2:A
+ * rimage_1_imeta
+ * segment1: striped: pv2:B
+ *
+ */
+
+static int _lv_add_integrity_to_raid(struct logical_volume *lv, const char *arg,
+ struct integrity_settings *settings,
+ struct dm_list *pvh,
+ uint64_t *data_sectors)
+{
+ struct lvcreate_params lp;
+ struct logical_volume *imeta_lvs[DEFAULT_RAID_MAX_IMAGES];
+ struct cmd_context *cmd = lv->vg->cmd;
+ struct volume_group *vg = lv->vg;
+ struct logical_volume *lv_image, *lv_imeta, *lv_iorig;
+ struct lv_segment *seg_top, *seg_image;
+ const struct segment_type *segtype;
+ struct integrity_settings *set;
+ uint64_t status_data_sectors = 0;
+ uint32_t area_count, s;
+ int internal_metadata;
+ int ret = 1;
+
+ memset(imeta_lvs, 0, sizeof(imeta_lvs));
+
+ if (dm_list_size(&lv->segments) != 1)
+ return_0;
+
+ seg_top = first_seg(lv);
+ area_count = seg_top->area_count;
+
+ if ((internal_metadata = !strcmp(arg, "internal"))) {
+ /*
+ * FIXME: raid on internal integrity might not be used widely
+ * enough to enable, given the additional complexity/support.
+ * i.e. nearly everyone may just use external metadata.
+ *
+ * FIXME: _info_run() needs code to adjust the length, like
+ * is done for if (lv_is_integrity()) length = ...
+ */
+ /* goto skip_imeta; */
+ log_error("Internal integrity metadata is not yet supported with raid.");
+ return 0;
+ }
+
+ /*
+ * For each rimage, create an _imeta LV for integrity metadata.
+ * Each needs to be zeroed.
+ */
+ for (s = 0; s < area_count; s++) {
+ struct logical_volume *meta_lv;
+ struct wipe_params wipe;
+
+ if (s >= DEFAULT_RAID_MAX_IMAGES)
+ return_0;
+
+ lv_image = seg_lv(seg_top, s);
+
+ if (!seg_is_striped(first_seg(lv_image))) {
+ log_error("raid1 image must be linear to add integrity");
+ return_0;
+ }
+
+ /*
+ * allocate a new linear LV NAME_rimage_N_imeta
+ * lv_create_integrity_metadata() returns its result in lp
+ */
+ memset(&lp, 0, sizeof(lp));
+ lp.lv_name = lv_image->name;
+ lp.pvh = pvh;
+ lp.extents = lv_image->size / vg->extent_size;
+
+ if (!lv_create_integrity_metadata(cmd, vg, &lp)) {
+ return_0;
+ }
+ meta_lv = lp.integrity_meta_lv;
+
+ /*
+ * dm-integrity requires the metadata LV header to be zeroed.
+ */
+
+ if (!activate_lv(cmd, meta_lv)) {
+ log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
+ return 0;
+ }
+
+ memset(&wipe, 0, sizeof(wipe));
+ wipe.do_zero = 1;
+ wipe.zero_sectors = 8;
+
+ if (!wipe_lv(meta_lv, wipe)) {
+ log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
+ return 0;
+ }
+
+ if (!deactivate_lv(cmd, meta_lv)) {
+ log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
+ return 0;
+ }
+
+ /* Used below to set up the new integrity segment. */
+ imeta_lvs[s] = meta_lv;
+ }
+
+/* skip_imeta: */
+
+ /*
+ * For each rimage, move its segments to a new rimage_iorig and give
+ * the rimage a new integrity segment.
+ */
+ for (s = 0; s < area_count; s++) {
+ lv_image = seg_lv(seg_top, s);
+
+ if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
+ return_0;
+
+ log_debug("Adding integrity to raid image %s", lv_image->name);
+
+ /*
+ * "lv_iorig" is a new LV with new id, but with the segments
+ * from "lv_image". "lv_image" keeps the existing name and id,
+ * but gets a new integrity segment, in place of the segments
+ * that were moved to lv_iorig.
+ */
+ if (!(lv_iorig = insert_layer_for_lv(cmd, lv_image, INTEGRITY, "_iorig")))
+ return_0;
+
+ lv_image->status |= INTEGRITY;
+
+ /*
+ * Set up the new first segment of lv_image as integrity.
+ */
+ seg_image = first_seg(lv_image);
+ seg_image->segtype = segtype;
+
+ if (!internal_metadata) {
+ lv_imeta = imeta_lvs[s]; /* external metadata lv created above */
+ lv_imeta->status |= INTEGRITY_METADATA;
+ lv_set_hidden(lv_imeta);
+ seg_image->integrity_data_sectors = lv_image->size;
+ seg_image->integrity_meta_dev = lv_imeta;
+ }
+
+ memcpy(&seg_image->integrity_settings, settings, sizeof(struct integrity_settings));
+ set = &seg_image->integrity_settings;
+
+ if (!set->mode[0])
+ set->mode[0] = DEFAULT_MODE;
+
+ if (!set->tag_size)
+ set->tag_size = DEFAULT_TAG_SIZE;
+
+ if (!set->internal_hash)
+ set->internal_hash = DEFAULT_INTERNAL_HASH;
+ }
+
+ /*
+ * When using internal metadata, we have to temporarily activate the
+ * integrity image with size 1 to get provided_data_sectors from the
+ * dm-integrity module.
+ */
+ if (internal_metadata) {
+ /* Get size from the first image, others will be the same. */
+ lv_image = seg_lv(seg_top, 0);
+
+ lv_image->status |= LV_TEMPORARY;
+ lv_image->status |= LV_NOSCAN;
+ lv_image->status |= LV_UNCOMMITTED;
+ seg_image = first_seg(lv_image);
+ seg_image->integrity_data_sectors = 1;
+
+ log_debug("Activating temporary integrity LV to get data sectors.");
+
+ if (!activate_lv(cmd, lv_image)) {
+ log_error("Failed to activate temporary integrity.");
+ ret = 0;
+ goto out;
+ }
+
+ if (!_get_provided_data_sectors(lv_image, &status_data_sectors)) {
+ log_error("Failed to get data sectors from dm-integrity");
+ ret = 0;
+ } else {
+ log_print("Found integrity provided_data_sectors %llu", (unsigned long long)status_data_sectors);
+ ret = 1;
+ }
+
+ if (!status_data_sectors) {
+ log_error("LV size too small to include metadata.");
+ ret = 0;
+ }
+
+ lv_image->status |= LV_UNCOMMITTED;
+
+ if (!deactivate_lv(cmd, lv_image)) {
+ log_error("Failed to deactivate temporary integrity.");
+ ret = 0;
+ }
+
+ if (!ret)
+ goto_out;
+
+ lv_image->status &= ~LV_UNCOMMITTED;
+ lv_image->status &= ~LV_NOSCAN;
+ lv_image->status &= ~LV_TEMPORARY;
+
+ /* The main point, setting integrity_data_sectors. */
+ for (s = 0; s < area_count; s++) {
+ lv_image = seg_lv(seg_top, s);
+ seg_image = first_seg(lv_image);
+ seg_image->integrity_data_sectors = status_data_sectors;
+ }
+ }
+
+ if (internal_metadata)
+ *data_sectors = status_data_sectors;
+ else
+ *data_sectors = lv->size;
+
+ log_debug("Write VG with integrity added to LV");
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ ret = 0;
+out:
+ return ret;
+}
+
+int lv_add_integrity(struct logical_volume *lv, const char *arg,
+ struct logical_volume *meta_lv_created,
+ const char *meta_name,
+ struct integrity_settings *settings,
+ struct dm_list *pvh,
+ uint64_t *data_sectors)
+{
+ struct cmd_context *cmd = lv->vg->cmd;
+ struct volume_group *vg = lv->vg;
+ struct integrity_settings *set;
+ struct logical_volume *lv_orig;
+ struct logical_volume *meta_lv = NULL;
+ const struct segment_type *segtype;
+ struct lv_segment *seg;
+ uint64_t meta_bytes, meta_sectors;
+ uint64_t lv_size_sectors;
+ int ret = 1;
+
+ if (lv_is_raid(lv))
+ return _lv_add_integrity_to_raid(lv, arg, settings, pvh, data_sectors);
+
+ lv_size_sectors = lv->size;
+
+ if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_INTEGRITY)))
+ return_0;
+
+ /*
+ * "lv_orig" is a new LV with new id, but with the segments from "lv".
+ * "lv" keeps the existing name and id, but gets a new integrity segment,
+ * in place of the segments that were moved to lv_orig.
+ */
+
+ if (!(lv_orig = insert_layer_for_lv(cmd, lv, INTEGRITY, "_iorig")))
+ return_0;
+
+ seg = first_seg(lv);
+ seg->segtype = segtype;
+
+ lv->status |= INTEGRITY;
+
+ memcpy(&seg->integrity_settings, settings, sizeof(struct integrity_settings));
+ set = &seg->integrity_settings;
+
+ if (!set->mode[0])
+ set->mode[0] = DEFAULT_MODE;
+
+ if (!set->tag_size)
+ set->tag_size = DEFAULT_TAG_SIZE;
+
+ if (!set->internal_hash)
+ set->internal_hash = DEFAULT_INTERNAL_HASH;
+
+ /*
+ * --integrity <arg> is y|external|internal
+ */
+
+ if (!arg)
+ arg = "external";
+
+ if (!strcmp(arg, "none")) {
+ log_error("Invalid --integrity arg for lvcreate.");
+ return 0;
+ }
+
+ if (!strcmp(arg, "internal") && meta_name) {
+ log_error("Internal integrity cannot be used with integritymetadata option.");
+ return 0;
+ }
+
+ if (meta_lv_created)
+ meta_lv = meta_lv_created;
+ else if (meta_name) {
+ if (!(meta_lv = find_lv(vg, meta_name))) {
+ log_error("LV %s not found.", meta_name);
+ return_0;
+ }
+
+ meta_bytes = _lv_size_bytes_to_integrity_meta_bytes(lv->size * 512);
+ meta_sectors = meta_bytes / 512;
+
+ if (meta_lv->size < meta_sectors) {
+ log_error("Integrity metadata needs %s, metadata LV is only %s.",
+ display_size(cmd, meta_sectors), display_size(cmd, meta_lv->size));
+ return 0;
+ }
+ }
+
+ /*
+ * When not using a meta_dev, dm-integrity needs to tell us what the
+ * usable size of the LV is, which is the dev size minus the integrity
+ * overhead. To find that, we need to do a special, temporary activation
+ * of the new LV, specifying a dm dev size of 1, then check the dm dev
+ * status field provided_data_sectors, which is the actual size of the
+ * LV. We need to include provided_data_sectors in the metadata for the
+ * new LV, and use this as the dm dev size for normal LV activation.
+ *
+ * When using a meta_dev, the dm dev size is the size of the data
+ * device. The necessary size of the meta_dev for the given data
+ * device needs to be estimated.
+ */
+
+ if (meta_lv) {
+ struct wipe_params wipe;
+
+ if (!sync_local_dev_names(cmd)) {
+ log_error("Failed to sync local devices.");
+ return 0;
+ }
+
+ log_verbose("Zeroing LV for integrity metadata");
+
+ if (!lv_is_active(meta_lv)) {
+ if (!activate_lv(cmd, meta_lv)) {
+ log_error("Failed to activate LV %s to zero", display_lvname(meta_lv));
+ return 0;
+ }
+ }
+
+ memset(&wipe, 0, sizeof(wipe));
+ wipe.do_zero = 1;
+ wipe.zero_sectors = 8;
+
+ if (!wipe_lv(meta_lv, wipe)) {
+ log_error("Failed to zero LV for integrity metadata %s", display_lvname(meta_lv));
+ return 0;
+ }
+
+ if (!deactivate_lv(cmd, meta_lv)) {
+ log_error("Failed to deactivate LV %s after zero", display_lvname(meta_lv));
+ return 0;
+ }
+
+ meta_lv->status |= INTEGRITY_METADATA;
+ seg->integrity_data_sectors = lv_size_sectors;
+ seg->integrity_meta_dev = meta_lv;
+ lv_set_hidden(meta_lv);
+ /* TODO: give meta_lv a suffix? e.g. _imeta */
+ } else {
+ /* dm-integrity wants temp/fake size of 1 to report usable size */
+ seg->integrity_data_sectors = 1;
+
+ lv->status |= LV_TEMPORARY;
+ lv->status |= LV_NOSCAN;
+ lv->status |= LV_UNCOMMITTED;
+
+ log_debug("Activating temporary integrity LV to get data sectors.");
+
+ if (!activate_lv(cmd, lv)) {
+ log_error("Failed to activate temporary integrity.");
+ ret = 0;
+ goto out;
+ }
+
+ if (!_get_provided_data_sectors(lv, &seg->integrity_data_sectors)) {
+ log_error("Failed to get data sectors from dm-integrity");
+ ret = 0;
+ } else {
+ log_debug("Found integrity provided_data_sectors %llu", (unsigned long long)seg->integrity_data_sectors);
+ ret = 1;
+ }
+
+ if (!seg->integrity_data_sectors) {
+ log_error("LV size too small to include metadata.");
+ ret = 0;
+ }
+
+ lv->status |= LV_UNCOMMITTED;
+
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Failed to deactivate temporary integrity.");
+ ret = 0;
+ }
+
+ lv->status &= ~LV_UNCOMMITTED;
+ lv->status &= ~LV_NOSCAN;
+ lv->status &= ~LV_TEMPORARY;
+ }
+
+ *data_sectors = seg->integrity_data_sectors;
+
+ log_debug("Write VG with integrity added to LV");
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ ret = 0;
+ out:
+ return ret;
+}
+
+
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 4c2ab2bbf..2ff9887d5 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -585,6 +585,8 @@ struct logical_volume *lv_origin_lv(const struct logical_volume *lv)
origin = first_seg(lv)->external_lv;
else if (lv_is_writecache(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
+ else if (lv_is_integrity(lv) && first_seg(lv)->origin)
+ origin = first_seg(lv)->origin;
return origin;
}
@@ -1200,10 +1202,13 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
else if (lv_is_pool_metadata(lv) ||
lv_is_pool_metadata_spare(lv) ||
- lv_is_raid_metadata(lv))
+ lv_is_raid_metadata(lv) ||
+ lv_is_integrity_metadata(lv))
repstr[0] = 'e';
else if (lv_is_cache_type(lv) || lv_is_writecache(lv))
repstr[0] = 'C';
+ else if (lv_is_integrity(lv))
+ repstr[0] = 'g';
else if (lv_is_raid(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
else if (lv_is_mirror(lv))
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index b4daa5633..798f3322d 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -28,6 +28,7 @@
#include "lib/datastruct/str_list.h"
#include "lib/config/defaults.h"
#include "lib/misc/lvm-exec.h"
+#include "lib/misc/lvm-signal.h"
#include "lib/mm/memlock.h"
#include "lib/locking/lvmlockd.h"
#include "lib/label/label.h"
@@ -55,6 +56,8 @@ typedef enum {
#define A_POSITIONAL_FILL 0x40 /* Slots are positional and filled using PREFERRED */
#define A_PARTITION_BY_TAGS 0x80 /* No allocated area may share any tag with any other */
+#define ONE_MB_IN_BYTES 1048576
+
/*
* Constant parameters during a single allocation attempt.
*/
@@ -134,7 +137,9 @@ enum {
LV_TYPE_SANLOCK,
LV_TYPE_CACHEVOL,
LV_TYPE_WRITECACHE,
- LV_TYPE_WRITECACHEORIGIN
+ LV_TYPE_WRITECACHEORIGIN,
+ LV_TYPE_INTEGRITY,
+ LV_TYPE_INTEGRITYORIGIN
};
static const char *_lv_type_names[] = {
@@ -190,6 +195,8 @@ static const char *_lv_type_names[] = {
[LV_TYPE_CACHEVOL] = "cachevol",
[LV_TYPE_WRITECACHE] = "writecache",
[LV_TYPE_WRITECACHEORIGIN] = "writecacheorigin",
+ [LV_TYPE_INTEGRITY] = "integrity",
+ [LV_TYPE_INTEGRITYORIGIN] = "integrityorigin",
};
static int _lv_layout_and_role_mirror(struct dm_pool *mem,
@@ -461,6 +468,43 @@ bad:
return 0;
}
+static int _lv_layout_and_role_integrity(struct dm_pool *mem,
+ const struct logical_volume *lv,
+ struct dm_list *layout,
+ struct dm_list *role,
+ int *public_lv)
+{
+ int top_level = 0;
+
+ /* non-top-level LVs */
+ if (lv_is_integrity_metadata(lv)) {
+ if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
+ !str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_METADATA]))
+ goto_bad;
+ } else if (lv_is_integrity_origin(lv)) {
+ if (!str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITY]) ||
+ !str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_ORIGIN]) ||
+ !str_list_add_no_dup_check(mem, role, _lv_type_names[LV_TYPE_INTEGRITYORIGIN]))
+ goto_bad;
+ } else
+ top_level = 1;
+
+ if (!top_level) {
+ *public_lv = 0;
+ return 1;
+ }
+
+ /* top-level LVs */
+ if (lv_is_integrity(lv)) {
+ if (!str_list_add_no_dup_check(mem, layout, _lv_type_names[LV_TYPE_INTEGRITY]))
+ goto_bad;
+ }
+
+ return 1;
+bad:
+ return 0;
+}
+
static int _lv_layout_and_role_thick_origin_snapshot(struct dm_pool *mem,
const struct logical_volume *lv,
struct dm_list *layout,
@@ -577,6 +621,11 @@ int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
!_lv_layout_and_role_cache(mem, lv, *layout, *role, &public_lv))
goto_bad;
+ /* Integrity related */
+ if ((lv_is_integrity(lv) || lv_is_integrity_origin(lv) || lv_is_integrity_metadata(lv)) &&
+ !_lv_layout_and_role_integrity(mem, lv, *layout, *role, &public_lv))
+ goto_bad;
+
/* VDO and related */
if (lv_is_vdo_type(lv) &&
!_lv_layout_and_role_vdo(mem, lv, *layout, *role, &public_lv))
@@ -1457,6 +1506,15 @@ static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
return_0;
}
+ if (delete && seg_is_integrity(seg)) {
+ /* Remove integrity origin in addition to integrity layer. */
+ if (!lv_remove(seg_lv(seg, 0)))
+ return_0;
+ /* Remove integrity metadata. */
+ if (seg->integrity_meta_dev && !lv_remove(seg->integrity_meta_dev))
+ return_0;
+ }
+
if ((pool_lv = seg->pool_lv)) {
if (!detach_pool_lv(seg))
return_0;
@@ -5654,6 +5712,11 @@ int lv_resize(struct logical_volume *lv,
return 0;
}
+ if (lv_is_integrity(lv)) {
+ log_error("Resize not yet allowed on LVs with integrity.");
+ return 0;
+ }
+
if (!_lvresize_check(lv, lp))
return_0;
@@ -7410,6 +7473,90 @@ int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
return 1;
}
+int zero_lv_name(struct cmd_context *cmd, const char *vg_name, const char *lv_name, uint64_t lv_size_sectors)
+{
+ char name[PATH_MAX];
+ struct device *dev;
+ uint64_t off = 0, i = 0, j = 0;
+ uint64_t lv_size_bytes = lv_size_sectors * 512;
+ uint64_t zero_bytes;
+ uint32_t extra_bytes;
+
+ if (dm_snprintf(name, sizeof(name), "%s%s/%s", cmd->dev_dir, vg_name, lv_name) < 0) {
+ log_error("Device path name too long, device not zeroed (%s).", lv_name);
+ return 0;
+ }
+
+ if (!(dev = dev_cache_get(cmd, name, NULL))) {
+ log_error("Failed to find device %s: device not zeroed.", name);
+ return 0;
+ }
+
+ if (!label_scan_open_rw(dev)) {
+ log_error("Failed to open %s: device not zeroed.", name);
+ return 0;
+ }
+
+ zero_bytes = lv_size_bytes;
+
+ log_print("Zeroing %s %s... (cancel command to zero manually)",
+ name, display_size(cmd, zero_bytes/512));
+
+ if ((extra_bytes = (zero_bytes % ONE_MB_IN_BYTES)))
+ zero_bytes -= extra_bytes;
+
+ /*
+ * Write 1MiB at a time to avoid going over bcache size.
+ * Then write 128KiB (bcache block sizes) at a time to
+ * cover remaining dev size.
+ */
+
+ sigint_allow();
+
+ for (i = 0; i < (zero_bytes / ONE_MB_IN_BYTES); i++) {
+ off = i * ONE_MB_IN_BYTES;
+ if (!dev_write_zeros(dev, off, (size_t)ONE_MB_IN_BYTES))
+ log_error("Failed to zero LV at offset %llu.", (unsigned long long)off);
+
+ if (off && !(off % (1024 * ONE_MB_IN_BYTES)))
+ log_print("Zeroed %s...", display_size(cmd, off/512));
+
+ if (sigint_caught()) {
+ log_print("Zeroing canceled.");
+ goto out;
+ }
+
+ }
+
+ if (extra_bytes) {
+ log_debug("Zeroing final %u bytes at %llu.", extra_bytes, (unsigned long long)off);
+
+ for (j = 0; j < (extra_bytes / BCACHE_BLOCK_SIZE_IN_BYTES); j++) {
+ off = i * ONE_MB_IN_BYTES + j * BCACHE_BLOCK_SIZE_IN_BYTES;
+ if (!dev_write_zeros(dev, off, (size_t)BCACHE_BLOCK_SIZE_IN_BYTES))
+ log_error("Failed to zero LV at offset %llu.", (unsigned long long)off);
+
+ if (sigint_caught()) {
+ log_print("Zeroing canceled.");
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * FIXME: bcache can't write partial blocks yet.
+ * This shouldn't actually happen given current
+ * usage where LV size is a multiple of extents.
+ */
+ if ((extra_bytes = lv_size_bytes - (i * ONE_MB_IN_BYTES + j * BCACHE_BLOCK_SIZE_IN_BYTES)))
+ log_warn("WARNING: last %llu bytes not zeroed.", (unsigned long long)extra_bytes);
+
+out:
+ sigint_restore();
+ label_scan_invalidate(dev);
+ return 1;
+}
+
/*
* Initialize the LV with 'value'.
*/
@@ -7679,6 +7826,15 @@ static int _should_wipe_lv(struct lvcreate_params *lp,
first_seg(first_seg(lv)->pool_lv)->zero_new_blocks))
return 0;
+ /*
+ * dm-integrity requires the first sector (integrity superblock) to be
+ * zero when creating a new integrity device.
+ * TODO: print a warning or error if the user specifically
+ * asks for no wiping or zeroing?
+ */
+ if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg))
+ return 1;
+
/* Cannot zero read-only volume */
if ((lv->status & LVM_WRITE) &&
(lp->zero || lp->wipe_signatures))
@@ -7934,6 +8090,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
/* FIXME Eventually support raid/mirrors with -m */
if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
return_0;
+
+ } else if (seg_is_integrity(lp)) {
+ if (!(create_segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_STRIPED)))
+ return_0;
+
} else if (seg_is_mirrored(lp) || (seg_is_raid(lp) && !seg_is_any_raid0(lp))) {
if (!(lp->region_size = adjusted_mirror_region_size(vg->cmd,
vg->extent_size,
@@ -8277,6 +8438,19 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
goto revert_new_lv;
}
lv->status &= ~LV_TEMPORARY;
+
+ } else if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
+ /*
+ * Activate the new origin LV so it can be zeroed/wiped
+ * below before adding integrity.
+ */
+ lv->status |= LV_TEMPORARY;
+ if (!activate_lv(cmd, lv)) {
+ log_error("Failed to activate new LV for wiping.");
+ goto revert_new_lv;
+ }
+ lv->status &= ~LV_TEMPORARY;
+
} else if (!lv_active_change(cmd, lv, lp->activate)) {
log_error("Failed to activate new LV %s.", display_lvname(lv));
goto deactivate_and_revert_new_lv;
@@ -8296,6 +8470,55 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
}
}
+ if (seg_is_integrity(lp) || (seg_is_raid1(lp) && lp->integrity_arg)) {
+ struct lv_activate_opts laopts = { 0 };
+ uint64_t integrity_data_sectors = 0;
+
+ log_verbose("Adding integrity to new LV");
+
+ /* Origin is active from zeroing, deactivate to add integrity. */
+
+ if (!deactivate_lv(cmd, lv)) {
+ log_error("Failed to deactivate LV to add integrity");
+ goto revert_new_lv;
+ }
+
+ if (!lv_add_integrity(lv, lp->integrity_arg, lp->integrity_meta_lv,
+ lp->integrity_meta_name, &lp->integrity_settings, lp->pvh,
+ &integrity_data_sectors))
+ goto revert_new_lv;
+
+ backup(vg);
+
+ /*
+ * The integrity metadata (checksums) need to be initialized.
+ * Activating the new integrity LVs with the recalculate
+ * option will cause dm-integrity to initialize.
+ *
+ * TODO: --integrityinit none|kernel|command
+ * . none: user will handle it (e.g. zero LV with dd),
+ * and we follow -an|-ay to decide about activating LV
+ * . kernel: activate LV with recalculate to start kernel init
+ * . command: activate LV for zeroing by this lvcreate command.
+ * if this command does not complete zeroing (e.g. user cancels
+ * the command or machine crashes), then the user must complete
+ * the the zeroing manually, or with a lvchange recalculate.
+ *
+ * For raid+integrity, recalculate by kernel is the only
+ * option that works.
+ */
+ laopts.integrity_recalculate = 1;
+
+ if (!activate_lv_opts(cmd, lv, &laopts)) {
+ log_error("Failed to activate LV %s.", display_lvname(lv));
+ log_error("The full LV should be zeroed to initialize integrity metadata.");
+ goto out;
+ }
+
+ log_print("LV %s activated to initialize integrity.", display_lvname(lv));
+ goto out;
+ }
+
if (seg_is_vdo_pool(lp)) {
if (!convert_vdo_pool_lv(lv, &lp->vdo_params, &lp->virtual_extents)) {
stack;
diff --git a/lib/metadata/merge.c b/lib/metadata/merge.c
index e482921ce..d6dd7a55a 100644
--- a/lib/metadata/merge.c
+++ b/lib/metadata/merge.c
@@ -742,6 +742,8 @@ int check_lv_segments(struct logical_volume *lv, int complete_vg)
seg_found++;
if (seg->metadata_lv == lv || seg->pool_lv == lv || seg->writecache == lv)
seg_found++;
+ if (seg->integrity_meta_dev == lv)
+ seg_found++;
if (seg_is_thin_volume(seg) && (seg->origin == lv || seg->external_lv == lv))
seg_found++;
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index c61c85c7e..927164510 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -84,12 +84,15 @@
#define CONVERTING UINT64_C(0x0000000000400000) /* LV */
#define MISSING_PV UINT64_C(0x0000000000800000) /* PV */
+#define INTEGRITY UINT64_C(0x0000000000800000) /* LV - Internal use only */
#define PV_MOVED_VG UINT64_C(0x4000000000000000) /* PV - Moved to a new VG */
#define PARTIAL_LV UINT64_C(0x0000000001000000) /* LV - derived flag, not
written out in metadata*/
//#define POSTORDER_FLAG UINT64_C(0x0000000002000000) /* Not real flags, reserved for
//#define POSTORDER_OPEN_FLAG UINT64_C(0x0000000004000000) temporary use inside vg_read_internal. */
+#define INTEGRITY_METADATA UINT64_C(0x0000000004000000) /* LV - Internal use only */
+#define LV_UNCOMMITTED UINT64_C(0x0000000002000000)
#define VIRTUAL_ORIGIN UINT64_C(0x0000000008000000) /* LV - internal use only */
#define MERGING UINT64_C(0x0000000010000000) /* LV SEG */
@@ -261,6 +264,8 @@
#define lv_is_pool_metadata_spare(lv) (((lv)->status & POOL_METADATA_SPARE) ? 1 : 0)
#define lv_is_lockd_sanlock_lv(lv) (((lv)->status & LOCKD_SANLOCK_LV) ? 1 : 0)
#define lv_is_writecache(lv) (((lv)->status & WRITECACHE) ? 1 : 0)
+#define lv_is_integrity(lv) (((lv)->status & INTEGRITY) ? 1 : 0)
+#define lv_is_integrity_metadata(lv) (((lv)->status & INTEGRITY_METADATA) ? 1 : 0)
#define lv_is_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
@@ -272,9 +277,11 @@
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
#define lv_is_component(lv) (lv_is_cache_origin(lv) || \
lv_is_writecache_origin(lv) || \
+ lv_is_integrity_origin(lv) || \
((lv)->status & (\
CACHE_POOL_DATA |\
CACHE_POOL_METADATA |\
+ INTEGRITY_METADATA |\
LV_CACHE_VOL |\
LV_VDO_POOL_DATA |\
MIRROR_IMAGE |\
@@ -519,6 +526,10 @@ struct lv_segment {
uint32_t writecache_block_size; /* For writecache */
struct writecache_settings writecache_settings; /* For writecache */
+ uint64_t integrity_data_sectors;
+ struct logical_volume *integrity_meta_dev;
+ struct integrity_settings integrity_settings;
+
struct dm_vdo_target_params vdo_params; /* For VDO-pool */
uint32_t vdo_pool_header_size; /* For VDO-pool */
uint32_t vdo_pool_virtual_extents; /* For VDO-pool */
@@ -992,6 +1003,12 @@ struct lvcreate_params {
alloc_policy_t alloc; /* all */
struct dm_vdo_target_params vdo_params; /* vdo */
+ const char *integrity_arg;
+ const char *integrity_meta_name; /* external LV is user-specified */
+ struct logical_volume *integrity_meta_lv; /* external LV we create */
+ struct integrity_settings integrity_settings;
+ uint64_t zero_data_sectors; /* the resulting size that should be zeroed at the end */
+
struct dm_list tags; /* all */
int yes;
@@ -1086,6 +1103,8 @@ int lv_is_cache_origin(const struct logical_volume *lv);
int lv_is_writecache_origin(const struct logical_volume *lv);
int lv_is_writecache_cachevol(const struct logical_volume *lv);
+int lv_is_integrity_origin(const struct logical_volume *lv);
+
int lv_is_merging_cow(const struct logical_volume *cow);
uint32_t cow_max_extents(const struct logical_volume *origin, uint32_t chunk_size);
int cow_has_min_chunks(const struct volume_group *vg, uint32_t cow_extents, uint32_t chunk_size);
@@ -1385,4 +1404,16 @@ int vg_is_foreign(struct volume_group *vg);
void vg_write_commit_bad_mdas(struct cmd_context *cmd, struct volume_group *vg);
+int lv_add_integrity(struct logical_volume *lv, const char *arg,
+ struct logical_volume *meta_lv_created,
+ const char *meta_name, struct integrity_settings *settings,
+ struct dm_list *pvh,
+ uint64_t *data_sectors);
+int lv_create_integrity_metadata(struct cmd_context *cmd,
+ struct volume_group *vg,
+ struct lvcreate_params *lp);
+int lv_remove_integrity(struct logical_volume *lv);
+
+int zero_lv_name(struct cmd_context *cmd, const char *vg_name, const char *lv_name, uint64_t lv_size_sectors);
+
#endif
diff --git a/lib/metadata/segtype.h b/lib/metadata/segtype.h
index 22a511eac..08ddc3565 100644
--- a/lib/metadata/segtype.h
+++ b/lib/metadata/segtype.h
@@ -67,6 +67,7 @@ struct dev_manager;
#define SEG_RAID6_N_6 (1ULL << 35)
#define SEG_RAID6 SEG_RAID6_ZR
#define SEG_WRITECACHE (1ULL << 36)
+#define SEG_INTEGRITY (1ULL << 37)
#define SEG_STRIPED_TARGET (1ULL << 39)
#define SEG_LINEAR_TARGET (1ULL << 40)
@@ -84,6 +85,7 @@ struct dev_manager;
#define SEG_TYPE_NAME_CACHE "cache"
#define SEG_TYPE_NAME_CACHE_POOL "cache-pool"
#define SEG_TYPE_NAME_WRITECACHE "writecache"
+#define SEG_TYPE_NAME_INTEGRITY "integrity"
#define SEG_TYPE_NAME_ERROR "error"
#define SEG_TYPE_NAME_FREE "free"
#define SEG_TYPE_NAME_ZERO "zero"
@@ -117,6 +119,7 @@ struct dev_manager;
#define segtype_is_cache(segtype) ((segtype)->flags & SEG_CACHE ? 1 : 0)
#define segtype_is_cache_pool(segtype) ((segtype)->flags & SEG_CACHE_POOL ? 1 : 0)
#define segtype_is_writecache(segtype) ((segtype)->flags & SEG_WRITECACHE ? 1 : 0)
+#define segtype_is_integrity(segtype) ((segtype)->flags & SEG_INTEGRITY ? 1 : 0)
#define segtype_is_mirrored(segtype) ((segtype)->flags & SEG_AREAS_MIRRORED ? 1 : 0)
#define segtype_is_mirror(segtype) ((segtype)->flags & SEG_MIRROR ? 1 : 0)
#define segtype_is_pool(segtype) ((segtype)->flags & (SEG_CACHE_POOL | SEG_THIN_POOL) ? 1 : 0)
@@ -179,6 +182,7 @@ struct dev_manager;
#define seg_is_cache(seg) segtype_is_cache((seg)->segtype)
#define seg_is_cache_pool(seg) segtype_is_cache_pool((seg)->segtype)
#define seg_is_writecache(seg) segtype_is_writecache((seg)->segtype)
+#define seg_is_integrity(seg) segtype_is_integrity((seg)->segtype)
#define seg_is_used_cache_pool(seg) (seg_is_cache_pool(seg) && (!dm_list_empty(&(seg->lv)->segs_using_this_lv)))
#define seg_is_linear(seg) (seg_is_striped(seg) && ((seg)->area_count == 1))
#define seg_is_mirror(seg) segtype_is_mirror((seg)->segtype)
@@ -347,6 +351,8 @@ int init_vdo_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
int init_writecache_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
+int init_integrity_segtypes(struct cmd_context *cmd, struct segtype_library *seglib);
+
#define CACHE_FEATURE_POLICY_MQ (1U << 0)
#define CACHE_FEATURE_POLICY_SMQ (1U << 1)
#define CACHE_FEATURE_METADATA2 (1U << 2)
diff --git a/lib/raid/raid.c b/lib/raid/raid.c
index e88a15408..48b63a31a 100644
--- a/lib/raid/raid.c
+++ b/lib/raid/raid.c
@@ -240,6 +240,26 @@ static int _raid_text_export(const struct lv_segment *seg, struct formatter *f)
return _raid_text_export_raid(seg, f);
}
+static int _image_integrity_data_sectors(struct lv_segment *seg, uint64_t *data_sectors)
+{
+ struct logical_volume *lv_image;
+ struct lv_segment *seg_image;
+ int s;
+
+ *data_sectors = 0;
+
+ for (s = 0; s < seg->area_count; s++) {
+ lv_image = seg_lv(seg, s);
+
+ if (lv_is_integrity(lv_image)) {
+ seg_image = first_seg(lv_image);
+ *data_sectors = seg_image->integrity_data_sectors;
+ return 1;
+ }
+ }
+ return 1;
+}
+
static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
struct dm_pool *mem __attribute__((unused)),
struct cmd_context *cmd __attribute__((unused)),
@@ -256,6 +276,7 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
uint64_t writemostly[RAID_BITMAP_SIZE] = { 0 };
struct dm_tree_node_raid_params_v2 params = { 0 };
unsigned attrs;
+ uint64_t integrity_data_sectors = 0;
if (seg_is_raid4(seg)) {
if (!_raid_target_present(cmd, NULL, &attrs) ||
@@ -351,6 +372,21 @@ static int _raid_add_target_line(struct dev_manager *dm __attribute__((unused)),
params.stripe_size = seg->stripe_size;
params.flags = flags;
+ /*
+ * The len needs to be reduced only when the raid images are using
+ * integrity with internal metadata, which reduces the usable
+ * size of the image, and needs to be reflected in the dm table
+ * that's loaded. (internal metadata is not currently allowed
+ * with raid, so this is unused.)
+ */
+ if (!_image_integrity_data_sectors(seg, &integrity_data_sectors))
+ return_0;
+ if (integrity_data_sectors) {
+ log_debug("Reducing raid size from %llu to integrity_data_sectors %llu",
+ (unsigned long long)len, (unsigned long long)integrity_data_sectors);
+ len = integrity_data_sectors;
+ }
+
if (!dm_tree_node_add_raid_target_with_params_v2(node, len, &params))
return_0;
diff --git a/tools/args.h b/tools/args.h
index 533cc5479..03202d99f 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -274,6 +274,15 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
"and \\fBdiff\\fP types include unsupported settings in their output by default,\n"
"all the other types ignore unsupported settings.\n")
+arg(integrity_ARG, '\0', "integrity", string_VAL, 0, 0,
+ "Controls if integrity metadata should be stored and checked for an LV.\n")
+
+arg(integritymetadata_ARG, '\0', "integritymetadata", lv_VAL, 0, 0,
+ "The name of an LV to hold integrity metadata.\n")
+
+arg(integritysettings_ARG, '\0', "integritysettings", string_VAL, ARG_GROUPABLE, 0,
+ "Set dm-integrity parameters.\n")
+
arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
"By default the PV is labelled with an LVM2 identifier in its second\n"
"sector (sector 1). This lets you use a different sector near the\n"
diff --git a/tools/command-lines.in b/tools/command-lines.in
index 10165ea8f..58ffa265f 100644
--- a/tools/command-lines.in
+++ b/tools/command-lines.in
@@ -757,6 +757,14 @@ FLAGS: SECONDARY_SYNTAX
---
+lvconvert --integrity String LV
+OO: OO_LVCONVERT
+OP: PV ...
+ID: lvconvert_integrity
+DESC: Remove integrity from an LV.
+
+---
+
# --extents is not specified; it's an automatic alternative for --size
OO_LVCREATE: --addtag Tag, --alloc Alloc, --autobackup Bool, --activate Active,
@@ -870,7 +878,8 @@ DESC: Create a raid1 or mirror LV (infers --type raid1|mirror).
# R9,R10,R11,R12 (--type raid with any use of --stripes/--mirrors)
lvcreate --type raid --size SizeMB VG
OO: --mirrors PNumber, --stripes Number, --stripesize SizeKB,
---regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB, OO_LVCREATE
+--regionsize RegionSize, --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
+--integrity String, --integritysettings String, OO_LVCREATE
OP: PV ...
ID: lvcreate_raid_any
DESC: Create a raid LV (a specific raid level must be used, e.g. raid1).
@@ -1269,6 +1278,20 @@ FLAGS: SECONDARY_SYNTAX
---
+lvcreate --type integrity --size SizeMB VG
+OO: --integrity String, --integritymetadata LV, --integritysettings String, OO_LVCREATE
+OP: PV ...
+ID: lvcreate_integrity
+DESC: Create a LV with integrity.
+
+lvcreate --integrity String --size SizeMB VG
+OO: --type integrity, --integritymetadata LV, --integritysettings String, OO_LVCREATE
+OP: PV ...
+ID: lvcreate_integrity
+DESC: Create a LV with integrity (infers --type integrity).
+
+---
+
lvdisplay
OO: --aligned, --all, --binary, --colon, --columns,
--configreport ConfigReport, --foreign, --history, --ignorelockingfailure,
diff --git a/tools/lv_types.h b/tools/lv_types.h
index 778cd541d..d1c94ccd8 100644
--- a/tools/lv_types.h
+++ b/tools/lv_types.h
@@ -34,5 +34,6 @@ lvt(raid10_LVT, "raid10", NULL)
lvt(error_LVT, "error", NULL)
lvt(zero_LVT, "zero", NULL)
lvt(writecache_LVT, "writecache", NULL)
+lvt(integrity_LVT, "integrity", NULL)
lvt(LVT_COUNT, "", NULL)
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index d0d277bcd..a4fa3e195 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -5730,6 +5730,75 @@ int lvconvert_to_cache_with_cachevol_cmd(struct cmd_context *cmd, int argc, char
return ret;
}
+static int _lvconvert_integrity_single(struct cmd_context *cmd,
+ struct logical_volume *lv,
+ struct processing_handle *handle)
+{
+ struct volume_group *vg = lv->vg;
+ struct lv_segment *seg;
+ const char *arg = arg_str_value(cmd, integrity_ARG, NULL);
+
+ if (strcmp(arg, "none") && strcmp(arg, "n")) {
+ log_error("Integrity can only be removed from an existing LV (see --integrity none).");
+ return ECMD_FAILED;
+ }
+
+ if (!lv_is_integrity(lv)) {
+ log_error("LV does not have integrity.");
+ return ECMD_FAILED;
+ }
+
+ seg = first_seg(lv);
+
+ if (!seg->integrity_meta_dev) {
+ log_error("Internal integrity cannot be removed.");
+ return ECMD_FAILED;
+ }
+
+ /* TODO: lift this restriction */
+ if (lv_info(cmd, lv, 1, NULL, 0, 0)) {
+ log_error("LV must be inactive to remove integrity.");
+ return ECMD_FAILED;
+ }
+
+ if (!archive(vg))
+ return ECMD_FAILED;
+
+ if (!lv_remove_integrity(lv))
+ return ECMD_FAILED;
+
+ if (!vg_write(vg) || !vg_commit(vg))
+ return ECMD_FAILED;
+
+ backup(vg);
+
+ log_print_unless_silent("Logical volume %s has removed integrity.", display_lvname(lv));
+ return ECMD_PROCESSED;
+}
+
+int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct processing_handle *handle;
+ struct lvconvert_result lr = { 0 };
+ int ret;
+
+ if (!(handle = init_processing_handle(cmd, NULL))) {
+ log_error("Failed to initialize processing handle.");
+ return ECMD_FAILED;
+ }
+
+ handle->custom_handle = &lr;
+
+ cmd->cname->flags &= ~GET_VGNAME_FROM_OPTIONS;
+
+ ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle, NULL,
+ &_lvconvert_integrity_single);
+
+ destroy_processing_handle(cmd, handle);
+
+ return ret;
+}
+
/*
* All lvconvert command defs have their own function,
* so the generic function name is unused.
diff --git a/tools/lvcreate.c b/tools/lvcreate.c
index 4a1534cc2..ff620f456 100644
--- a/tools/lvcreate.c
+++ b/tools/lvcreate.c
@@ -792,6 +792,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
mirror_default_cfg = (arg_uint_value(cmd, stripes_ARG, 1) > 1)
? global_raid10_segtype_default_CFG : global_mirror_segtype_default_CFG;
segtype_str = find_config_tree_str(cmd, mirror_default_cfg, NULL);
+ } else if (arg_is_set(cmd, integrity_ARG)) {
+ segtype_str = SEG_TYPE_NAME_INTEGRITY;
} else
segtype_str = SEG_TYPE_NAME_STRIPED;
@@ -826,6 +828,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
readahead_ARG,\
setactivationskip_ARG,\
test_ARG,\
+ integrity_ARG,\
+ integritysettings_ARG,\
type_ARG
#define CACHE_POOL_ARGS \
@@ -1225,6 +1229,11 @@ static int _lvcreate_params(struct cmd_context *cmd,
}
}
+ if (seg_is_integrity(lp) || seg_is_raid(lp)) {
+ if (!get_integrity_options(cmd, &lp->integrity_arg, &lp->integrity_meta_name, &lp->integrity_settings))
+ return 0;
+ }
+
lcp->pv_count = argc;
lcp->pvs = argv;
@@ -1582,6 +1591,13 @@ static int _check_zero_parameters(struct cmd_context *cmd, struct lvcreate_param
if (seg_is_thin(lp))
return 1;
+ if (seg_is_integrity(lp)) {
+ if (lp->zero && !is_change_activating(lp->activate)) {
+ log_error("Zeroing integrity is not compatible with inactive creation (-an).");
+ return 0;
+ }
+ }
+
/* If there is some problem, buffer will not be empty */
if (dm_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
lp->origin_name ? "origin " : "",
@@ -1709,6 +1725,24 @@ static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name,
lp->pool_name ? : "with generated name", lp->vg_name, lp->segtype->name);
}
+ /*
+ * Create an LV to hold integrity metadata when:
+ *
+ * . The user wants external (not internal) metadata. External
+ * is indicated by: no option set (external is default), or
+ * --integrity y|external.
+ *
+ * . The user did not specify an existing metadata LV with
+ * --integritymetadata, saved as lp->integrity_meta_name.
+ */
+ if (seg_is_integrity(lp) && !lp->integrity_meta_name &&
+ (!lp->integrity_arg ||
+ !strcmp(lp->integrity_arg, "external") ||
+ !strcmp(lp->integrity_arg, "y"))) {
+ if (!lv_create_integrity_metadata(cmd, vg, lp))
+ goto_out;
+ }
+
if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) {
if (!handle_sanlock_lv(cmd, vg)) {
log_error("No space for sanlock lock, extend the internal lvmlock LV.");
@@ -1782,5 +1816,9 @@ int lvcreate(struct cmd_context *cmd, int argc, char **argv)
_destroy_lvcreate_params(&lp);
destroy_processing_handle(cmd, handle);
+
+ if (lp.zero_data_sectors)
+ zero_lv_name(cmd, lp.vg_name, lp.lv_name, lp.zero_data_sectors);
+
return ret;
}
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index 3d79e9739..5f5557603 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -149,6 +149,9 @@ static const struct command_function _command_functions[CMD_COUNT] = {
{ lvconvert_to_vdopool_CMD, lvconvert_to_vdopool_cmd },
{ lvconvert_to_vdopool_param_CMD, lvconvert_to_vdopool_param_cmd },
+ /* lvconvert for integrity */
+ { lvconvert_integrity_CMD, lvconvert_integrity_cmd },
+
{ pvscan_display_CMD, pvscan_display_cmd },
{ pvscan_cache_CMD, pvscan_cache_cmd },
};
diff --git a/tools/toollib.c b/tools/toollib.c
index a5304bf63..baff0976b 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1414,6 +1414,174 @@ out:
return ok;
}
+
+static int _get_one_integrity_setting(struct cmd_context *cmd, struct integrity_settings *settings,
+ char *key, char *val)
+{
+ if (!strncmp(key, "mode", strlen("mode"))) {
+ if (*val == 'D')
+ settings->mode[0] = 'D';
+ else if (*val == 'J')
+ settings->mode[0] = 'J';
+ else if (*val == 'B')
+ settings->mode[0] = 'B';
+ else if (*val == 'R')
+ settings->mode[0] = 'R';
+ else
+ goto_bad;
+ /* lvm assigns a default if the user doesn't. */
+ return 1;
+ }
+
+ if (!strncmp(key, "tag_size", strlen("tag_size"))) {
+ if (sscanf(val, "%u", &settings->tag_size) != 1)
+ goto_bad;
+ /* lvm assigns a default if the user doesn't. */
+ return 1;
+ }
+
+ if (!strncmp(key, "internal_hash", strlen("internal_hash"))) {
+ if (!(settings->internal_hash = strdup(val)))
+ goto_bad;
+ /* lvm assigns a default if the user doesn't. */
+ return 1;
+ }
+
+ /*
+ * For the following settings, lvm does not set a default value if the
+ * user does not specify their own value, and lets dm-integrity use its
+ * own default.
+ */
+
+ if (!strncmp(key, "journal_sectors", strlen("journal_sectors"))) {
+ if (sscanf(val, "%u", &settings->journal_sectors) != 1)
+ goto_bad;
+ settings->journal_sectors_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "interleave_sectors", strlen("interleave_sectors"))) {
+ if (sscanf(val, "%u", &settings->interleave_sectors) != 1)
+ goto_bad;
+ settings->interleave_sectors_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "buffer_sectors", strlen("buffer_sectors"))) {
+ if (sscanf(val, "%u", &settings->buffer_sectors) != 1)
+ goto_bad;
+ settings->buffer_sectors_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "journal_watermark", strlen("journal_watermark"))) {
+ if (sscanf(val, "%u", &settings->journal_watermark) != 1)
+ goto_bad;
+ settings->journal_watermark_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "commit_time", strlen("commit_time"))) {
+ if (sscanf(val, "%u", &settings->commit_time) != 1)
+ goto_bad;
+ settings->commit_time_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "block_size", strlen("block_size"))) {
+ if (sscanf(val, "%u", &settings->block_size) != 1)
+ goto_bad;
+ settings->block_size_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "bitmap_flush_interval", strlen("bitmap_flush_interval"))) {
+ if (sscanf(val, "%u", &settings->bitmap_flush_interval) != 1)
+ goto_bad;
+ settings->bitmap_flush_interval_set = 1;
+ return 1;
+ }
+
+ if (!strncmp(key, "sectors_per_bit", strlen("sectors_per_bit"))) {
+ if (sscanf(val, "%llu", (unsigned long long *)&settings->sectors_per_bit) != 1)
+ goto_bad;
+ settings->sectors_per_bit_set = 1;
+ return 1;
+ }
+
+ log_error("Unknown setting: %s", key);
+ return 0;
+
+ bad:
+ log_error("Invalid setting: %s", key);
+ return 0;
+}
+
+static int _get_integrity_settings(struct cmd_context *cmd, struct integrity_settings *settings)
+{
+ struct arg_value_group_list *group;
+ const char *str;
+ char key[64];
+ char val[64];
+ int num;
+ int pos;
+
+ /*
+ * "grouped" means that multiple --integritysettings options can be used.
+ * Each option is also allowed to contain multiple key = val pairs.
+ */
+
+ dm_list_iterate_items(group, &cmd->arg_value_groups) {
+ if (!grouped_arg_is_set(group->arg_values, integritysettings_ARG))
+ continue;
+
+ if (!(str = grouped_arg_str_value(group->arg_values, integritysettings_ARG, NULL)))
+ break;
+
+ pos = 0;
+
+ while (pos < strlen(str)) {
+ /* scan for "key1=val1 key2 = val2 key3= val3" */
+
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+
+ if (sscanf(str + pos, " %63[^=]=%63s %n", key, val, &num) != 2) {
+ log_error("Invalid setting at: %s", str+pos);
+ return 0;
+ }
+
+ pos += num;
+
+ if (!_get_one_integrity_setting(cmd, settings, key, val))
+ return_0;
+ }
+ }
+
+ return 1;
+}
+
+int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
+ struct integrity_settings *set)
+{
+ *arg = NULL;
+ *meta_name = NULL;
+ memset(set, 0, sizeof(struct integrity_settings));
+
+ if (arg_is_set(cmd, integrity_ARG))
+ *arg = arg_str_value(cmd, integrity_ARG, NULL);
+
+ if (arg_is_set(cmd, integritymetadata_ARG))
+ *meta_name = arg_str_value(cmd, integritymetadata_ARG, NULL);
+
+ if (arg_is_set(cmd, integritysettings_ARG)) {
+ if (!_get_integrity_settings(cmd, set))
+ return_0;
+ }
+
+ return 1;
+}
+
/* FIXME move to lib */
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
{
@@ -2579,6 +2747,8 @@ static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int l
return seg_is_raid10(seg);
case writecache_LVT:
return seg_is_writecache(seg);
+ case integrity_LVT:
+ return seg_is_integrity(seg);
case error_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
case zero_LVT:
@@ -2637,6 +2807,8 @@ int get_lvt_enum(struct logical_volume *lv)
return raid10_LVT;
if (seg_is_writecache(seg))
return writecache_LVT;
+ if (seg_is_integrity(seg))
+ return integrity_LVT;
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
return error_LVT;
diff --git a/tools/tools.h b/tools/tools.h
index a2baaa5da..ab5866466 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -234,6 +234,9 @@ struct lv_prop *get_lv_prop(int lvp_enum);
struct lv_type *get_lv_type(int lvt_enum);
struct command *get_command(int cmd_enum);
+int get_integrity_options(struct cmd_context *cmd, const char **arg, const char **meta_name,
+ struct integrity_settings *set);
+
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv);
@@ -273,6 +276,8 @@ int lvconvert_merge_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_cmd(struct cmd_context *cmd, int argc, char **argv);
int lvconvert_to_vdopool_param_cmd(struct cmd_context *cmd, int argc, char **argv);
+int lvconvert_integrity_cmd(struct cmd_context *cmd, int argc, char **argv);
+
int pvscan_display_cmd(struct cmd_context *cmd, int argc, char **argv);
int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv);