mirror of
git://sourceware.org/git/lvm2.git
synced 2026-01-25 00:32:58 +03:00
Compare commits
1 Commits
dev-dct-lo
...
dev-dct-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a66c49a199 |
@@ -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,35 @@ 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);
|
||||
|
||||
/*
|
||||
* VDO target
|
||||
|
||||
@@ -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,10 @@ 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 */
|
||||
};
|
||||
|
||||
/* Per-device properties */
|
||||
@@ -2705,6 +2711,82 @@ 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 (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 (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 +2971,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 +2987,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 +3825,39 @@ 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)
|
||||
{
|
||||
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));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dm_tree_node_add_replicator_target(struct dm_tree_node *node,
|
||||
uint64_t size,
|
||||
const char *rlog_uuid,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -2870,6 +2870,7 @@ int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
|
||||
int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *active_lv;
|
||||
const struct logical_volume *lv_use;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@@ -2888,19 +2889,30 @@ int activate_lv(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lv->status & LV_UNCOMMITTED)
|
||||
lv_use = lv;
|
||||
else
|
||||
lv_use = lv_committed(lv);
|
||||
|
||||
ret = lv_activate_with_filter(cmd, NULL, 0,
|
||||
(lv->status & LV_NOSCAN) ? 1 : 0,
|
||||
(lv->status & LV_TEMPORARY) ? 1 : 0,
|
||||
lv_committed(lv));
|
||||
lv_use);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,8 +104,10 @@ static const struct flag _lv_flags[] = {
|
||||
{LV_VDO_POOL, NULL, 0},
|
||||
{LV_VDO_POOL_DATA, NULL, 0},
|
||||
{WRITECACHE, NULL, 0},
|
||||
{INTEGRITY, NULL, 0},
|
||||
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
|
||||
{LV_REMOVED, NULL, 0},
|
||||
{LV_UNCOMMITTED, NULL, 0},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
325
lib/integrity/integrity.c
Normal file
325
lib/integrity/integrity.c
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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 && !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 __attribute__((unused)),
|
||||
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))
|
||||
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;
|
||||
}
|
||||
@@ -1045,15 +1045,6 @@ int label_scan(struct cmd_context *cmd)
|
||||
*/
|
||||
_prepare_open_file_limit(cmd, dm_list_size(&scan_devs));
|
||||
|
||||
/*
|
||||
* Read and save the timestamps from VG lock files. The lock file
|
||||
* timestamp is updated when a command releases an exclusive lock
|
||||
* (indicating it has changed the VG.) So, the timestamps can be
|
||||
* checked later to detect if another command has changed the VG since
|
||||
* our label scan.
|
||||
*/
|
||||
file_lock_save_times(cmd);
|
||||
|
||||
/*
|
||||
* Do the main scan.
|
||||
*/
|
||||
|
||||
@@ -26,14 +26,12 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static char _lock_dir[PATH_MAX];
|
||||
|
||||
static void _fin_file_locking(void)
|
||||
{
|
||||
release_flocks(1);
|
||||
free_flocks();
|
||||
}
|
||||
|
||||
static void _reset_file_locking(void)
|
||||
@@ -52,12 +50,11 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
|
||||
log_error("Too long locking filename %s/P_%s.", _lock_dir, resource + 1);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
if (dm_snprintf(lockfile, sizeof(lockfile), "%s/V_%s", _lock_dir, resource) < 0) {
|
||||
log_error("Too long locking filename %s/V_%s.", _lock_dir, resource);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lock_file(lockfile, flags))
|
||||
return_0;
|
||||
@@ -98,72 +95,3 @@ int init_file_locking(struct locking_type *locking, struct cmd_context *cmd,
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each file in locking_dir with V_ and no aux,
|
||||
* stat and save the file time.
|
||||
*/
|
||||
|
||||
void file_lock_save_times(struct cmd_context *cmd)
|
||||
{
|
||||
char lockfile[PATH_MAX];
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *aux;
|
||||
|
||||
if (!(dir = opendir(_lock_dir)))
|
||||
return;
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (de->d_name[0] != 'V')
|
||||
continue;
|
||||
if ((aux = strchr(de->d_name, ':'))) {
|
||||
if (!strncmp(aux, ":aux", 4))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dm_snprintf(lockfile, sizeof(lockfile), "%s/%s", _lock_dir, de->d_name) < 0)
|
||||
continue;
|
||||
|
||||
lock_file_time_init(lockfile);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the file lock timestamp is unchanged from when it was saved by
|
||||
* file_lock_save_times. Return true if unchanged. Return false if the
|
||||
* timestamp is different, or if there's no info to know.
|
||||
*/
|
||||
bool file_lock_time_unchanged(struct cmd_context *cmd, const char *resource)
|
||||
{
|
||||
char lockfile[PATH_MAX];
|
||||
|
||||
/* shouldn't be used with this function */
|
||||
if (!strcmp(resource, VG_GLOBAL))
|
||||
return false;
|
||||
|
||||
if (dm_snprintf(lockfile, sizeof(lockfile), "%s/V_%s", _lock_dir, resource) < 0) {
|
||||
log_error("Too long locking filename %s/V_%s.", _lock_dir, resource);
|
||||
return false;
|
||||
}
|
||||
|
||||
return lock_file_time_unchanged(lockfile);
|
||||
}
|
||||
|
||||
void file_lock_remove_on_unlock(struct cmd_context *cmd, const char *resource)
|
||||
{
|
||||
char lockfile[PATH_MAX];
|
||||
|
||||
/* shouldn't be used with this function */
|
||||
if (!strcmp(resource, VG_GLOBAL))
|
||||
return;
|
||||
|
||||
if (dm_snprintf(lockfile, sizeof(lockfile), "%s/V_%s", _lock_dir, resource) < 0) {
|
||||
log_error("Too long locking filename %s/V_%s.", _lock_dir, resource);
|
||||
return;
|
||||
}
|
||||
|
||||
lock_file_remove_on_unlock(lockfile);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,4 @@ int lockf_global_convert(struct cmd_context *cmd, const char *mode);
|
||||
int lock_global(struct cmd_context *cmd, const char *mode);
|
||||
int lock_global_convert(struct cmd_context *cmd, const char *mode);
|
||||
|
||||
void file_lock_save_times(struct cmd_context *cmd);
|
||||
bool file_lock_time_unchanged(struct cmd_context *cmd, const char *resource);
|
||||
void file_lock_remove_on_unlock(struct cmd_context *cmd, const char *resource);
|
||||
|
||||
#endif
|
||||
|
||||
200
lib/metadata/integrity_manip.c
Normal file
200
lib/metadata/integrity_manip.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
static int _get_provided_data_sectors(struct logical_volume *lv, uint64_t *provided_data_sectors)
|
||||
{
|
||||
struct lv_with_info_and_seg_status status;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
*provided_data_sectors = status.seg_status.integrity->provided_data_sectors;
|
||||
|
||||
dm_pool_destroy(status.seg_status.mem);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
dm_pool_destroy(status.seg_status.mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lv_add_integrity(struct logical_volume *lv, const char *arg, const char *meta_name,
|
||||
struct integrity_settings *settings)
|
||||
{
|
||||
struct cmd_context *cmd = lv->vg->cmd;
|
||||
struct integrity_settings *set;
|
||||
struct logical_volume *lv_orig;
|
||||
struct logical_volume *meta_lv = NULL;
|
||||
const struct segment_type *segtype;
|
||||
struct lv_segment *seg;
|
||||
int ret;
|
||||
|
||||
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 = "internal"; /* FIXME: make default 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 ((!strcmp(arg, "external") || !strcmp(arg, "y")) && !meta_name) {
|
||||
/* FIXME: allocate an LV for metadata */
|
||||
log_error("External integrity requires integritymetadata option.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (meta_name) {
|
||||
if (!(meta_lv = find_lv(lv->vg, meta_name))) {
|
||||
log_error("LV %s not found.", meta_name);
|
||||
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) {
|
||||
seg->integrity_data_sectors = seg->len;
|
||||
seg->integrity_meta_dev = meta_lv;
|
||||
lv_set_hidden(meta_lv);
|
||||
/* TODO: give meta_lv a suffix? e.g. _imeta */
|
||||
ret = 1;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1457,6 +1457,12 @@ static int _lv_reduce(struct logical_volume *lv, uint32_t extents, int delete)
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* Remove integrity origin in addition to integrity layer. */
|
||||
if (delete && seg_is_integrity(seg)) {
|
||||
if (!lv_remove(seg_lv(seg, 0)))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if ((pool_lv = seg->pool_lv)) {
|
||||
if (!detach_pool_lv(seg))
|
||||
return_0;
|
||||
@@ -5654,6 +5660,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 +7421,12 @@ int insert_layer_for_segments_on_pv(struct cmd_context *cmd,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: copied from label.c */
|
||||
#define BCACHE_BLOCK_SIZE_IN_SECTORS 256 /* 256*512 = 128K */
|
||||
#define BCACHE_BLOCK_SIZE_IN_BYTES 131072
|
||||
#define ONE_MB_IN_BYTES 1048576
|
||||
#define ONE_MB_IN_SECTORS 2048 /* 2048 * 512 = 1048576 */
|
||||
|
||||
/*
|
||||
* Initialize the LV with 'value'.
|
||||
*/
|
||||
@@ -7468,7 +7485,44 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
stack;
|
||||
}
|
||||
|
||||
if (wp.do_zero) {
|
||||
if (wp.do_zero && !wp.zero_value && (wp.zero_sectors >= ONE_MB_IN_SECTORS)) {
|
||||
uint64_t off = 0, i = 0, j = 0;
|
||||
uint64_t zero_bytes;
|
||||
uint32_t extra_bytes;
|
||||
|
||||
zero_bytes = wp.zero_sectors * 512;
|
||||
|
||||
if ((extra_bytes = (zero_bytes % ONE_MB_IN_BYTES)))
|
||||
zero_bytes -= extra_bytes;
|
||||
|
||||
log_print("Zeroing %llu MiB...", (unsigned long long)(zero_bytes / ONE_MB_IN_BYTES));
|
||||
|
||||
/*
|
||||
* Write 1MiB at a time to avoid going over bcache size.
|
||||
* Then write 128KiB at a time to cover remaining dev size.
|
||||
*/
|
||||
|
||||
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 (extra_bytes) {
|
||||
log_warn("Zeroing %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);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: bcache can't write partial block yet */
|
||||
if ((extra_bytes = (wp.zero_sectors * 512) - (i * ONE_MB_IN_BYTES + j * BCACHE_BLOCK_SIZE_IN_BYTES)))
|
||||
log_warn("WARNING: last %llu bytes not zeroed.", (unsigned long long)extra_bytes);
|
||||
|
||||
} else if (wp.do_zero) {
|
||||
zero_sectors = wp.zero_sectors ? : UINT64_C(4096) >> SECTOR_SHIFT;
|
||||
|
||||
if (zero_sectors > lv->size)
|
||||
@@ -7679,6 +7733,11 @@ static int _should_wipe_lv(struct lvcreate_params *lp,
|
||||
first_seg(first_seg(lv)->pool_lv)->zero_new_blocks))
|
||||
return 0;
|
||||
|
||||
if (seg_is_integrity(lp) && (!lp->zero || !(lv->status & LVM_WRITE))) {
|
||||
log_warn("WARNING: --zero not yet, integrity not initialized and may cause read errors.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cannot zero read-only volume */
|
||||
if ((lv->status & LVM_WRITE) &&
|
||||
(lp->zero || lp->wipe_signatures))
|
||||
@@ -7934,6 +7993,21 @@ 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)) {
|
||||
/*
|
||||
* TODO: if using internal metadata, estimate the amount of metadata
|
||||
* that will be needed, and add this to the amount of PV space being
|
||||
* allocated so that the usable LV size is what the user requested.
|
||||
*
|
||||
* TODO: if using external metadata, and no external metadata LV was
|
||||
* specified, then after allocating this LV, allocate another LV for
|
||||
* metadata. The size of the metadata LV we allocate also needs to
|
||||
* be estimated.
|
||||
*/
|
||||
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,
|
||||
@@ -8158,6 +8232,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_is_integrity(lp)) {
|
||||
if (!lv_add_integrity(lv, lp->integrity_arg, lp->integrity_meta_name, &lp->integrity_settings))
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
lv_set_activation_skip(lv, lp->activation_skip & ACTIVATION_SKIP_SET,
|
||||
lp->activation_skip & ACTIVATION_SKIP_SET_ENABLED);
|
||||
/*
|
||||
@@ -8282,7 +8361,22 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
if (_should_wipe_lv(lp, lv, !lp->suppress_zero_warn)) {
|
||||
if (seg_is_integrity(lp)) {
|
||||
struct wipe_params wipe;
|
||||
|
||||
memset(&wipe, 0, sizeof(wipe));
|
||||
wipe.do_zero = 1;
|
||||
wipe.zero_sectors = first_seg(lv)->integrity_data_sectors;
|
||||
|
||||
if (!_should_wipe_lv(lp, lv, 1))
|
||||
goto_out;
|
||||
|
||||
if (!wipe_lv(lv, wipe))
|
||||
log_error("Failed to zero LV.");
|
||||
|
||||
goto out;
|
||||
|
||||
} else if (_should_wipe_lv(lp, lv, !lp->suppress_zero_warn)) {
|
||||
if (!wipe_lv(lv, (struct wipe_params)
|
||||
{
|
||||
.do_zero = lp->zero,
|
||||
|
||||
@@ -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++;
|
||||
|
||||
|
||||
@@ -84,12 +84,14 @@
|
||||
#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 LV_UNCOMMITTED UINT64_C(0x0000000002000000)
|
||||
#define VIRTUAL_ORIGIN UINT64_C(0x0000000008000000) /* LV - internal use only */
|
||||
|
||||
#define MERGING UINT64_C(0x0000000010000000) /* LV SEG */
|
||||
@@ -261,6 +263,7 @@
|
||||
#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_vdo(lv) (((lv)->status & LV_VDO) ? 1 : 0)
|
||||
#define lv_is_vdo_pool(lv) (((lv)->status & LV_VDO_POOL) ? 1 : 0)
|
||||
@@ -519,6 +522,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 +999,10 @@ 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;
|
||||
struct integrity_settings integrity_settings;
|
||||
|
||||
struct dm_list tags; /* all */
|
||||
|
||||
int yes;
|
||||
@@ -1385,4 +1396,6 @@ 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, const char *meta_name, struct integrity_settings *settings);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4586,7 +4586,6 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
struct device *mda_dev, *dev_ret, *dev;
|
||||
struct cached_vg_fmtdata *vg_fmtdata = NULL; /* Additional format-specific data about the vg */
|
||||
struct pv_list *pvl;
|
||||
const char *rescan_reason = NULL;
|
||||
int found_old_metadata = 0;
|
||||
int found_md_component = 0;
|
||||
unsigned use_previous_vg;
|
||||
@@ -4623,27 +4622,16 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
*
|
||||
* The devs in the VG may be persistently inconsistent due to some
|
||||
* previous problem. In this case, rescanning the labels here will
|
||||
* find the same inconsistency.
|
||||
* find the same inconsistency. The VG repair (mistakenly done by
|
||||
* vg_read below) is supposed to fix that.
|
||||
*
|
||||
* Even though it's acceptable to report old metadata that was scanned
|
||||
* prior to taking the VG lock, we can actually detect the rare case
|
||||
* when another command has written the metadata between the time of
|
||||
* our scan and us acquiring the VG lock. We save the VG lock file
|
||||
* timestamp prior to scan, then check it again aftrr taking the VG
|
||||
* lock (file_lock_time_unchanged). If the timestamp is different, it
|
||||
* means that another command has written the metadata since our scan,
|
||||
* and we rescan it here to report the latest metadata.
|
||||
* FIXME: sort out the usage of the global lock (which is mixed up
|
||||
* with the orphan lock), and when we can tell that the global
|
||||
* lock is taken prior to the label scan, and still held here,
|
||||
* we can also skip the rescan in that case.
|
||||
*/
|
||||
|
||||
if (!cmd->can_use_one_scan)
|
||||
rescan_reason = "disabled";
|
||||
else if (lvmcache_scan_mismatch(cmd, vgname, vgid))
|
||||
rescan_reason = "mismatch";
|
||||
else if (!file_lock_time_unchanged(cmd, vgname))
|
||||
rescan_reason = "changed";
|
||||
|
||||
if (rescan_reason) {
|
||||
log_debug_metadata("Rescanning devices for %s %s (%s)", vgname, writing ? "rw" : "", rescan_reason);
|
||||
if (!cmd->can_use_one_scan || lvmcache_scan_mismatch(cmd, vgname, vgid)) {
|
||||
log_debug_metadata("Rescanning devices for %s %s", vgname, writing ? "rw" : "");
|
||||
if (writing)
|
||||
lvmcache_label_rescan_vg_rw(cmd, vgname, vgid);
|
||||
else
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -25,16 +25,36 @@
|
||||
struct lock_list {
|
||||
struct dm_list list;
|
||||
int lf;
|
||||
unsigned ex:1;
|
||||
unsigned remove_on_unlock:1;
|
||||
char *res;
|
||||
struct timespec save_time;
|
||||
};
|
||||
|
||||
static struct dm_list _lock_list;
|
||||
static int _prioritise_write_locks;
|
||||
|
||||
#define AUX_LOCK_SUFFIX ":aux"
|
||||
/* Drop lock known to be shared with another file descriptor. */
|
||||
static void _drop_shared_flock(const char *file, int fd)
|
||||
{
|
||||
log_debug_locking("_drop_shared_flock %s.", file);
|
||||
|
||||
if (close(fd) < 0)
|
||||
log_sys_debug("close", file);
|
||||
}
|
||||
|
||||
static void _undo_flock(const char *file, int fd)
|
||||
{
|
||||
struct stat buf1, buf2;
|
||||
|
||||
log_debug_locking("_undo_flock %s", file);
|
||||
if (!flock(fd, LOCK_NB | LOCK_EX) &&
|
||||
!stat(file, &buf1) &&
|
||||
!fstat(fd, &buf2) &&
|
||||
is_same_inode(buf1, buf2))
|
||||
if (unlink(file))
|
||||
log_sys_debug("unlink", file);
|
||||
|
||||
if (close(fd) < 0)
|
||||
log_sys_debug("close", file);
|
||||
}
|
||||
|
||||
static struct lock_list *_get_lock_list_entry(const char *file)
|
||||
{
|
||||
@@ -50,19 +70,6 @@ static struct lock_list *_get_lock_list_entry(const char *file)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _unlink_aux(const char *file)
|
||||
{
|
||||
char aux_path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(aux_path, sizeof(aux_path), "%s%s", file, AUX_LOCK_SUFFIX) < 0) {
|
||||
stack;
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlink(aux_path))
|
||||
log_sys_debug("unlink", aux_path);
|
||||
}
|
||||
|
||||
static int _release_lock(const char *file, int unlock)
|
||||
{
|
||||
struct lock_list *ll;
|
||||
@@ -71,54 +78,18 @@ static int _release_lock(const char *file, int unlock)
|
||||
dm_list_iterate_safe(llh, llt, &_lock_list) {
|
||||
ll = dm_list_item(llh, struct lock_list);
|
||||
|
||||
if (ll->lf < 0)
|
||||
continue;
|
||||
|
||||
if (!file || !strcmp(ll->res, file)) {
|
||||
/*
|
||||
* When a VG is being removed, and the flock is still
|
||||
* held for the VG, it sets the remove_on_unlock flag,
|
||||
* so that when the flock is unlocked, the lock file is
|
||||
* then also removed.
|
||||
*/
|
||||
if (file && unlock && ll->remove_on_unlock) {
|
||||
log_debug("Unlocking %s and removing", ll->res);
|
||||
|
||||
if (_prioritise_write_locks)
|
||||
_unlink_aux(ll->res);
|
||||
if (flock(ll->lf, LOCK_NB | LOCK_UN))
|
||||
log_sys_debug("flock", ll->res);
|
||||
if (unlink(ll->res))
|
||||
log_sys_debug("unlink", ll->res);
|
||||
if (close(ll->lf) < 0)
|
||||
log_sys_debug("close", ll->res);
|
||||
|
||||
dm_list_del(&ll->list);
|
||||
free(ll->res);
|
||||
free(ll);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the lock file timestamp when unlocking an
|
||||
* exclusive flock. Other commands may use the
|
||||
* timestamp change to detect that the VG was changed.
|
||||
*/
|
||||
if (file && unlock && ll->ex) {
|
||||
if (futimens(ll->lf, NULL) < 0)
|
||||
log_debug("lock file %s time update error %d", file, errno);
|
||||
}
|
||||
|
||||
dm_list_del(llh);
|
||||
if (unlock) {
|
||||
log_very_verbose("Unlocking %s", ll->res);
|
||||
if (flock(ll->lf, LOCK_NB | LOCK_UN))
|
||||
log_sys_debug("flock", ll->res);
|
||||
}
|
||||
_undo_flock(ll->res, ll->lf);
|
||||
} else
|
||||
_drop_shared_flock(ll->res, ll->lf);
|
||||
|
||||
if (close(ll->lf) < 0)
|
||||
log_sys_debug("close", ll->res);
|
||||
|
||||
ll->lf = -1;
|
||||
free(ll->res);
|
||||
free(llh);
|
||||
|
||||
if (file)
|
||||
return 1;
|
||||
@@ -183,6 +154,8 @@ static int _do_flock(const char *file, int *fd, int operation, uint32_t nonblock
|
||||
return_0;
|
||||
}
|
||||
|
||||
#define AUX_LOCK_SUFFIX ":aux"
|
||||
|
||||
static int _do_write_priority_flock(const char *file, int *fd, int operation, uint32_t nonblock)
|
||||
{
|
||||
int r, fd_aux = -1;
|
||||
@@ -194,11 +167,9 @@ static int _do_write_priority_flock(const char *file, int *fd, int operation, ui
|
||||
if ((r = _do_flock(file_aux, &fd_aux, LOCK_EX, 0))) {
|
||||
if (operation == LOCK_EX) {
|
||||
r = _do_flock(file, fd, operation, nonblock);
|
||||
if (close(fd_aux) < 0)
|
||||
log_sys_debug("close", file_aux);
|
||||
_undo_flock(file_aux, fd_aux);
|
||||
} else {
|
||||
if (close(fd_aux) < 0)
|
||||
log_sys_debug("close", file_aux);
|
||||
_undo_flock(file_aux, fd_aux);
|
||||
r = _do_flock(file, fd, operation, nonblock);
|
||||
}
|
||||
}
|
||||
@@ -212,7 +183,6 @@ int lock_file(const char *file, uint32_t flags)
|
||||
uint32_t nonblock = flags & LCK_NONBLOCK;
|
||||
uint32_t convert = flags & LCK_CONVERT;
|
||||
int r;
|
||||
int ex = 0;
|
||||
struct lock_list *ll;
|
||||
char state;
|
||||
|
||||
@@ -224,7 +194,6 @@ int lock_file(const char *file, uint32_t flags)
|
||||
case LCK_WRITE:
|
||||
operation = LOCK_EX;
|
||||
state = 'W';
|
||||
ex = 1;
|
||||
break;
|
||||
case LCK_UNLOCK:
|
||||
return _release_lock(file, 1);
|
||||
@@ -241,27 +210,22 @@ int lock_file(const char *file, uint32_t flags)
|
||||
log_very_verbose("Locking %s %c%c convert", ll->res, state,
|
||||
nonblock ? ' ' : 'B');
|
||||
r = flock(ll->lf, operation);
|
||||
if (!r) {
|
||||
ll->ex = ex;
|
||||
if (!r)
|
||||
return 1;
|
||||
}
|
||||
log_error("Failed to convert flock on %s %d", file, errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ll = _get_lock_list_entry(file))) {
|
||||
if (!(ll = zalloc(sizeof(struct lock_list))))
|
||||
return_0;
|
||||
if (!(ll = malloc(sizeof(struct lock_list))))
|
||||
return_0;
|
||||
|
||||
if (!(ll->res = strdup(file))) {
|
||||
free(ll);
|
||||
return_0;
|
||||
}
|
||||
|
||||
ll->lf = -1;
|
||||
dm_list_add(&_lock_list, &ll->list);
|
||||
if (!(ll->res = strdup(file))) {
|
||||
free(ll);
|
||||
return_0;
|
||||
}
|
||||
|
||||
ll->lf = -1;
|
||||
|
||||
log_very_verbose("Locking %s %c%c", ll->res, state,
|
||||
nonblock ? ' ' : 'B');
|
||||
|
||||
@@ -273,9 +237,12 @@ int lock_file(const char *file, uint32_t flags)
|
||||
(void) dm_prepare_selinux_context(NULL, 0);
|
||||
|
||||
if (r)
|
||||
ll->ex = ex;
|
||||
else
|
||||
dm_list_add(&_lock_list, &ll->list);
|
||||
else {
|
||||
free(ll->res);
|
||||
free(ll);
|
||||
stack;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -287,93 +254,3 @@ void init_flock(struct cmd_context *cmd)
|
||||
_prioritise_write_locks =
|
||||
find_config_tree_bool(cmd, global_prioritise_write_locks_CFG, NULL);
|
||||
}
|
||||
|
||||
void free_flocks(void)
|
||||
{
|
||||
struct lock_list *ll, *ll2;
|
||||
|
||||
dm_list_iterate_items_safe(ll, ll2, &_lock_list) {
|
||||
dm_list_del(&ll->list);
|
||||
free(ll->res);
|
||||
free(ll);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the lock file timestamps prior to scanning, so that the timestamps can
|
||||
* be checked later (lock_file_time_unchanged) to see if the VG has been
|
||||
* changed.
|
||||
*/
|
||||
|
||||
void lock_file_time_init(const char *file)
|
||||
{
|
||||
struct lock_list *ll;
|
||||
struct stat buf;
|
||||
|
||||
if (stat(file, &buf) < 0)
|
||||
return;
|
||||
|
||||
if (!(ll = _get_lock_list_entry(file))) {
|
||||
if (!(ll = zalloc(sizeof(struct lock_list))))
|
||||
return;
|
||||
|
||||
if (!(ll->res = strdup(file))) {
|
||||
free(ll);
|
||||
return;
|
||||
}
|
||||
|
||||
ll->lf = -1;
|
||||
ll->save_time = buf.st_mtim;
|
||||
dm_list_add(&_lock_list, &ll->list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a lock file timestamp has been changed (by other command) since we
|
||||
* saved it (lock_file_time_init). Another command may have updated the lock
|
||||
* file timestamp when releasing an ex flock (futimens above.)
|
||||
*/
|
||||
|
||||
bool lock_file_time_unchanged(const char *file)
|
||||
{
|
||||
struct lock_list *ll;
|
||||
struct stat buf;
|
||||
struct timespec *prev, *now;
|
||||
|
||||
if (stat(file, &buf) < 0) {
|
||||
log_debug("lock_file_time_unchanged no file %s", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(ll = _get_lock_list_entry(file))) {
|
||||
log_debug("lock_file_time_unchanged no list item %s", file);
|
||||
return false;
|
||||
}
|
||||
|
||||
prev = &ll->save_time;
|
||||
now = &buf.st_mtim;
|
||||
|
||||
if ((now->tv_sec == prev->tv_sec) && (now->tv_nsec == prev->tv_nsec)) {
|
||||
log_debug("lock file %s unchanged from %llu.%llu", file,
|
||||
(unsigned long long)prev->tv_sec,
|
||||
(unsigned long long)prev->tv_nsec);
|
||||
return true;
|
||||
}
|
||||
|
||||
log_debug("lock file %s changed from %llu.%llu to %llu.%llu", file,
|
||||
(unsigned long long)prev->tv_sec,
|
||||
(unsigned long long)prev->tv_nsec,
|
||||
(unsigned long long)now->tv_sec,
|
||||
(unsigned long long)now->tv_nsec);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void lock_file_remove_on_unlock(const char *file)
|
||||
{
|
||||
struct lock_list *ll;
|
||||
|
||||
if ((ll = _get_lock_list_entry(file)))
|
||||
ll->remove_on_unlock = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,7 @@
|
||||
#define _LVM_FLOCK_H
|
||||
|
||||
void init_flock(struct cmd_context *cmd);
|
||||
void free_flocks(void);
|
||||
int lock_file(const char *file, uint32_t flags);
|
||||
void release_flocks(int unlock);
|
||||
|
||||
void lock_file_time_init(const char *file);
|
||||
bool lock_file_time_unchanged(const char *file);
|
||||
void lock_file_remove_on_unlock(const char *file);
|
||||
|
||||
#endif /* _LVM_FLOCK_H */
|
||||
|
||||
@@ -272,6 +272,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"
|
||||
|
||||
@@ -1269,6 +1269,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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -785,6 +785,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;
|
||||
|
||||
@@ -1218,6 +1220,11 @@ static int _lvcreate_params(struct cmd_context *cmd,
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_is_integrity(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;
|
||||
|
||||
|
||||
172
tools/toollib.c
172
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;
|
||||
|
||||
@@ -233,6 +233,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);
|
||||
|
||||
@@ -78,8 +78,6 @@ static int _vgremove_single(struct cmd_context *cmd, const char *vg_name,
|
||||
|
||||
lockd_free_vg_final(cmd, vg);
|
||||
|
||||
file_lock_remove_on_unlock(cmd, vg->name);
|
||||
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user