1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-29 13:44:18 +03:00

Compare commits

..

9 Commits

Author SHA1 Message Date
David Teigland
8f6df67ee3 lvconvert: add hidden lv access restrictions
This copies the previous logic which is probably
not correct.
2016-12-01 16:04:33 -06:00
David Teigland
8248a7a266 lvconvert: remove unused calls for snapshots
snapshot commands are no longer called from the
monolithic lvconvert code, so remove the unused code.
2016-12-01 16:04:33 -06:00
David Teigland
cacf570932 lvconvert: snapshot: use command definitions
Lift all the snapshot utilities (merge, split, combine)
out of the monolithic lvconvert implementation, using
the command definitions.  The old code associated with
these commands is now unused and will be removed separately.
2016-12-01 16:04:30 -06:00
David Teigland
f1ed4d269c lvconvert: remove unused calls for repair and replace
repair and replace are no longer called from the
monolithic lvconvert code, so remove the unused code.
2016-12-01 15:51:24 -06:00
David Teigland
12605dd4d7 lvconvert: repair and replace: use command definitions
This lifts the lvconvert --repair and --replace commands
out of the monolithic lvconvert implementation.  The
previous calls into repair/replace can no longer be
reached and will be removed in a separate commit.
2016-12-01 15:51:24 -06:00
David Teigland
ecaa806fa6 lvchange: make use of command definitions
Reorganize the lvchange code to take advantage of
the command definition, and remove the validation
that is done by the command definintion rules.
2016-12-01 15:51:24 -06:00
David Teigland
d658ddfc70 process_each_lv: command def validation for LV in other position
Determine which position arg the LV being processed is.
Use this position to check the LV against the command def.
2016-12-01 15:51:24 -06:00
David Teigland
268374c235 process_each_lv: add check_single_lv function
The new check_single_lv() function is called prior to the
existing process_single_lv().  If the check function returns 0,
the LV will not be processed.

The check_single_lv function is meant to be a standard method
to validate the combination of specific command + specific LV,
and decide if the combination is allowed.  The check_single
function can be used by anything that calls process_each_lv.

As commands are migrated to take advantage of command
definitions, each command definition gets its own entry
point which calls process_each for itself, passing a
pair of check_single/process_single functions which can
be specific to the narrowly defined command def.
2016-11-30 16:37:29 -06:00
David Teigland
45e23131b8 commands: new method for defining commands
. Define a prototype for every lvm command.
. Match every user command with one definition.
. Generate help text and man pages from them.

The new file command-lines.in defines a prototype for every
unique lvm command.  A unique lvm command is a unique
combination of: command name + required option args +
required positional args.  Each of these prototypes also
includes the optional option args and optional positional
args that the command will accept, a description, and a
unique string ID for the definition.  Any valid command
will match one of the prototypes.

Here's an example of the lvresize command definitions from
command-lines.in, there are three unique lvresize commands:

lvresize --size SizeMB LV
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync, --reportformat String, --resizefs,
--stripes Number, --stripesize SizeKB, --poolmetadatasize SizeMB
OP: PV ...
ID: lvresize_by_size
DESC: Resize an LV by a specified size.

lvresize LV PV ...
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --resizefs, --stripes Number, --stripesize SizeKB
ID: lvresize_by_pv
DESC: Resize an LV by specified PV extents.
FLAGS: SECONDARY_SYNTAX

lvresize --poolmetadatasize SizeMB LV_thinpool
OO: --alloc Alloc, --autobackup Bool, --force,
--nofsck, --nosync, --noudevsync,
--reportformat String, --stripes Number, --stripesize SizeKB
OP: PV ...
ID: lvresize_pool_metadata_by_size
DESC: Resize a pool metadata SubLV by a specified size.

The three commands have separate definitions because they have
different required parameters.  Required parameters are specified
on the first line of the definition.  Optional options are
listed after OO, and optional positional args are listed after OP.

This data is used to generate corresponding command definition
structures for lvm in command-lines.h.  usage/help output is also
auto generated, so it is always in sync with the definitions.

Example of the corresponding generated structure in
command-lines.h for the first lvresize prototype
(these structures are never edited directly):

commands[83].name = "lvresize";
commands[83].command_line_id = "lvresize_by_size";
commands[83].command_line_enum = lvresize_by_size_CMD;
commands[83].fn = lvresize;
commands[83].ro_count = 1;
commands[83].rp_count = 1;
commands[83].oo_count = 22;
commands[83].op_count = 1;
commands[83].cmd_flags = 0;
commands[83].desc = "DESC: Resize an LV by a specified size.";
commands[83].usage = "lvresize --size Number[m|unit] LV"
" [ --resizefs, --poolmetadatasize Number[m|unit], COMMON_OPTIONS ]"
" [ PV ... ]";
commands[83].usage_common =
" [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit, --nosync, --reportformat String, --autobackup y|n, --stripes Number, --stripesize Number[k|unit], --nofsck, --commandprofile String, --config String, --debug, --driverloaded y|n, --help, --profile String, --quiet, --verbose, --version, --yes, --test, --force, --noudevsync ]";
commands[83].required_opt_args[0].opt = size_ARG;
commands[83].required_opt_args[0].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].required_pos_args[0].pos = 1;
commands[83].required_pos_args[0].def.val_bits = val_enum_to_bit(lv_VAL);
commands[83].optional_opt_args[0].opt = commandprofile_ARG;
commands[83].optional_opt_args[0].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[1].opt = config_ARG;
commands[83].optional_opt_args[1].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[2].opt = debug_ARG;
commands[83].optional_opt_args[3].opt = driverloaded_ARG;
commands[83].optional_opt_args[3].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[4].opt = help_ARG;
commands[83].optional_opt_args[5].opt = profile_ARG;
commands[83].optional_opt_args[5].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[6].opt = quiet_ARG;
commands[83].optional_opt_args[7].opt = verbose_ARG;
commands[83].optional_opt_args[8].opt = version_ARG;
commands[83].optional_opt_args[9].opt = yes_ARG;
commands[83].optional_opt_args[10].opt = test_ARG;
commands[83].optional_opt_args[11].opt = alloc_ARG;
commands[83].optional_opt_args[11].def.val_bits = val_enum_to_bit(alloc_VAL);
commands[83].optional_opt_args[12].opt = autobackup_ARG;
commands[83].optional_opt_args[12].def.val_bits = val_enum_to_bit(bool_VAL);
commands[83].optional_opt_args[13].opt = force_ARG;
commands[83].optional_opt_args[14].opt = nofsck_ARG;
commands[83].optional_opt_args[15].opt = nosync_ARG;
commands[83].optional_opt_args[16].opt = noudevsync_ARG;
commands[83].optional_opt_args[17].opt = reportformat_ARG;
commands[83].optional_opt_args[17].def.val_bits = val_enum_to_bit(string_VAL);
commands[83].optional_opt_args[18].opt = resizefs_ARG;
commands[83].optional_opt_args[19].opt = stripes_ARG;
commands[83].optional_opt_args[19].def.val_bits = val_enum_to_bit(number_VAL);
commands[83].optional_opt_args[20].opt = stripesize_ARG;
commands[83].optional_opt_args[20].def.val_bits = val_enum_to_bit(sizekb_VAL);
commands[83].optional_opt_args[21].opt = poolmetadatasize_ARG;
commands[83].optional_opt_args[21].def.val_bits = val_enum_to_bit(sizemb_VAL);
commands[83].optional_pos_args[0].pos = 2;
commands[83].optional_pos_args[0].def.val_bits = val_enum_to_bit(pv_VAL);
commands[83].optional_pos_args[0].def.flags = ARG_DEF_FLAG_MAY_REPEAT;

Every user-entered command is compared against the set of
command structures, and matched with one.  An error is
reported if an entered command does not have the required
parameters for any definition.  The closest match is printed
as a suggestion, and running lvresize --help will display
the usage for each possible lvresize command.

The prototype syntax used for help/man output includes
required --option and positional args on the first line,
and optional --option and positional args enclosed in [ ]
on subsequent lines.

  command_name <required_opt_args> <required_pos_args>
          [ <optional_opt_args> ]
          [ <optional_pos_args> ]

$ lvresize --help
  lvresize - Resize a logical volume

  Resize an LV by a specified size.
  lvresize --size Number[m|unit] LV
        [ --resizefs,
          --poolmetadatasize Number[m|unit],
          COMMON_OPTIONS ]
        [ PV ... ]

  Resize a pool metadata SubLV by a specified size.
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool
        [ COMMON_OPTIONS ]
        [ PV ... ]

  Common options:
        [ --alloc contiguous|cling|cling_by_tags|normal|anywhere|inherit,
          --nosync,
          --reportformat String,
          --autobackup y|n,
          --stripes Number,
          --stripesize Number[k|unit],
          --nofsck,
          --commandprofile String,
          --config String,
          --debug,
          --driverloaded y|n,
          --help,
          --profile String,
          --quiet,
          --verbose,
          --version,
          --yes,
          --test,
          --force,
          --noudevsync ]

  (Use --help --help for usage notes.)

$ lvresize --poolmetadatasize 4
  Failed to find a matching command definition.
  Closest command usage is:
  lvresize --poolmetadatasize Number[m|unit] LV_thinpool

Command definitions that are not to be advertised/suggested
have the flag SECONDARY_SYNTAX.  These commands will not be
printed in the normal help output.

Man page prototypes are also generated from the same original
command definitions, and are always in sync with the code
and help text.

Very early in command execution, a matching command definition
is found.  lvm then knows the operation being done, and that
the provided args conform to the definition.  This will allow
lots of ad hoc checking/validation to be removed throughout
the code.

Each command definition can also be routed to a specific
function to implement it.  The function is associated with
an enum value for the command definition (generated from
the ID string.)  These per-command-definition implementation
functions have not yet been created, so all commands
currently fall back to the existing per-command-name
implementation functions.

Using per-command-definition functions will allow lots of
code to be removed which tries to figure out what the
command is meant to do.  This is currently based on ad hoc
and complicated option analysis.  When using the new
functions, what the command is doing is already known
from the associated command definition.

So, this first phase validates every user-entered command
against the set of command prototypes, then calls the existing
implementation.  The second phase can associate an implementation
function with each definition, and take further advantage of the
known operation to avoid the complicated option analysis.
2016-11-30 16:37:20 -06:00
17 changed files with 469 additions and 315 deletions

View File

@@ -54,7 +54,7 @@ mkdir test_mnt
setup_merge_ $vg1 $lv1
mount "$(lvdev_ $vg1 $lv1)" test_mnt
lvconvert --merge $vg1/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg1/$(snap_lv_name_ $lv1)
umount test_mnt
vgchange -an $vg1

View File

@@ -34,7 +34,7 @@ snap_and_merge() {
SLEEP_PID=$!
# initiate background merge
lvconvert -b --merge $vg/$lv2
lvconvert -b --mergesnapshot $vg/$lv2
lvs -a -o+lv_merging,lv_merge_failed $vg
kill $SLEEP_PID

View File

@@ -51,15 +51,15 @@ mkdir test_mnt
# test full merge of a single LV
setup_merge_ $vg $lv1
# make sure lvconvert --merge requires explicit LV listing
not lvconvert --merge
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
# make sure lvconvert --mergesnapshot requires explicit LV listing
not lvconvert --mergesnapshot
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
lvremove -f $vg/$lv1
# test that an actively merging snapshot may not be removed
setup_merge_ $vg $lv1
lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1)
lvconvert -i+100 --mergesnapshot --background $vg/$(snap_lv_name_ $lv1)
not lvremove -f $vg/$(snap_lv_name_ $lv1)
lvremove -f $vg/$lv1
@@ -67,7 +67,7 @@ lvremove -f $vg/$lv1
# "onactivate merge" test
setup_merge_ $vg $lv1
mount "$(lvdev_ $vg $lv1)" test_mnt
lvconvert --merge $vg/$(snap_lv_name_ $lv1)
lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1)
# -- refresh LV while FS is still mounted (merge must not start),
# verify 'snapshot-origin' target is still being used
lvchange --refresh $vg/$lv1

View File

@@ -102,7 +102,7 @@ lvcreate -s -n snap $vg/$lv1
lvcreate -s -L10 -n oldsnapof_${lv1} $vg/$lv1
not lvconvert --merge $vg/snap
$MKFS "$DM_DEV_DIR/$vg/oldsnapof_${lv1}"
lvconvert --merge $vg/oldsnapof_${lv1}
lvconvert --mergesnapshot $vg/oldsnapof_${lv1}
fsck -n "$DM_DEV_DIR/$vg/$lv1"
check lv_not_exists $vg oldsnapof_${lv1}
# Add old snapshot to thin snapshot

View File

@@ -240,7 +240,7 @@ OO_LVCHANGE_META: --addtag Tag, --deltag Tag,
--writebehind Number, --writemostly WriteMostlyPV, --persistent n
lvchange OO_LVCHANGE_META VG|LV|Tag|Select ...
OO: OO_LVCHANGE
OO: --activate Active, OO_LVCHANGE
ID: lvchange_properties
DESC: Change a general LV property.
RULE: all not lv_is_pvmove lv_is_mirror_log lv_is_mirror_image
@@ -275,15 +275,14 @@ ID: lvchange_rebuild
DESC: Reconstruct data on specific PVs of a raid LV.
RULE: all not LV_raid0
# try removing the META change options from here?
lvchange --activate Active VG|LV|Tag|Select ...
OO: --activationmode ActivationMode, --partial, --ignoreactivationskip,
--ignorelockingfailure, --sysinit, OO_LVCHANGE_META, OO_LVCHANGE
--ignorelockingfailure, --sysinit, OO_LVCHANGE
ID: lvchange_activate
DESC: Activate or deactivate an LV.
lvchange --refresh VG|LV|Tag|Select ...
OO: --partial, OO_LVCHANGE
OO: --partial, --poll Bool, OO_LVCHANGE
ID: lvchange_refresh
DESC: Reactivate an LV using the latest metadata.
@@ -470,9 +469,6 @@ FLAGS: SECONDARY_SYNTAX
---
# lvconvert utilities related to snapshots and repair.
# Create a new command set for these and migrate them out of lvconvert?
# FIXME: lvconvert --merge is an extremely ambiguous command.
# It can do very different operations, but which one depends
# on knowing the LV type. So, the command doesn't know what
@@ -503,34 +499,58 @@ ID: lvconvert_merge
DESC: Merge LV that was previously split from a mirror.
DESC: Merge thin LV into its origin LV.
DESC: Merge COW snapshot LV into its origin.
RULE: all not lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
---
# lvconvert snapshot-related utilities
# Create a new command set for these and migrate them out of lvconvert?
lvconvert --mergesnapshot LV_snapshot ...
OO: --background, --interval Number, OO_LVCONVERT
ID: lvconvert_merge_snapshot
DESC: Merge LV that was previously split from a mirror.
RULE: all not lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow
---
# NB: an unsual use of position args here, where
# the second position LV is the only one processed
# by process_each_lv.
lvconvert --splitsnapshot LV_snapshot
OO: OO_LVCONVERT
ID: lvconvert_split_cow_snapshot
DESC: Separate a COW snapshot from its origin LV.
RULE: all not lv_is_locked lv_is_pvmove lv_is_origin lv_is_external_origin lv_is_merging_cow
---
# NB: an unsual use of position args here, the first pos arg
# (will become origin LV) is not passed to process_each,
# the second pos arg (will become cow LV) is given to
# process_each. Because the first pos LV is not handled
# by process_each_lv, it cannot be checked against this
# command def, so a specific LV type in the first pos
# will not be checked.
# alternate form of lvconvert --snapshot
lvconvert --type snapshot LV_linear_striped_raid LV_snapshot
lvconvert --type snapshot LV LV_linear
OO: --snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT
ID: lvconvert_combine_split_snapshot
DESC: Combine LV with a previously split snapshot LV.
DESC: Combine a former COW snapshot (second arg) with a former
DESC: origin LV (first arg) to reverse a splitsnapshot command.
FLAGS: SECONDARY_SYNTAX
RULE: all not lv_is_locked lv_is_pvmove
lvconvert --snapshot LV_linear_striped_raid LV_snapshot
lvconvert --snapshot LV LV_linear
OO: --type snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT
ID: lvconvert_combine_split_snapshot
DESC: Combine LV with a previously split snapshot LV.
DESC: Combine a former COW snapshot (second arg) with a former
DESC: origin LV (first arg) to reverse a splitsnapshot command.
RULE: all not lv_is_locked lv_is_pvmove
---
# lvconvert repair/replace utilitiles
# Create a new command set for these and migrate them out of lvconvert?
# FIXME: use specific option names to distinguish these two
# very different commands, e.g.
#
@@ -551,22 +571,14 @@ OP: PV ...
ID: lvconvert_repair_pvs_or_thinpool
DESC: Replace failed PVs in a raid or mirror LV.
DESC: Repair a thin pool.
---
RULE: all not lv_is_locked lv_is_pvmove
lvconvert --replace PV LV_raid
OO: OO_LVCONVERT
OP: PV ...
ID: lvconvert_replace_pv
DESC: Replace specific PV(s) in a raid* LV with another PV.
---
lvconvert --splitsnapshot LV_snapshot
OO: OO_LVCONVERT
ID: lvconvert_split_cow_snapshot
DESC: Separate a COW snapshot from its origin LV.
RULE: all not lv_is_origin lv_is_external_origin lv_is_merging_cow lv_is_vg_writable lv_is_pvmove
RULE: all not lv_is_locked lv_is_pvmove
---

View File

@@ -28,5 +28,7 @@ lvt(raid4_LVT, "raid4", NULL)
lvt(raid5_LVT, "raid5", NULL)
lvt(raid6_LVT, "raid6", NULL)
lvt(raid10_LVT, "raid10", NULL)
lvt(error_LVT, "error", NULL)
lvt(zero_LVT, "zero", NULL)
lvt(LVT_COUNT, "", NULL)

View File

@@ -1009,14 +1009,56 @@ static int _lvchange_properties_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_properties_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
int lvchange_properties_cmd(struct cmd_context *cmd, int argc, char **argv)
{
int ret;
/*
* A command def rule allows only some options when LV is partial,
* so handles_missing_pvs will only affect those.
*/
cmd->handles_missing_pvs = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL, _lvchange_properties_single);
ret = process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvchange_properties_check, &_lvchange_properties_single);
/*
* Unfortunately, lvchange has previously allowed changing an LV
* property *and* changing LV activation in a single command. This was
* not a good idea because the behavior/results are hard to predict and
* not possible to sensibly describe. It's also unnecessary. So, this
* is here for the sake of compatibility.
*
* This is extremely ugly; activation should always be done separately.
* This is not the full-featured lvchange capability, just the basic
* (the advanced activate options are not provided.)
*
* FIXME: wrap this in a config setting that we can disable by default
* to phase this out?
*/
if (arg_is_set(cmd, activate_ARG)) {
log_warn("WARNING: Combined property change and activation change is not advised.");
ret = lvchange_activate_cmd(cmd, argc, argv);
}
return ret;
}
static int _lvchange_activate_single(struct cmd_context *cmd,
@@ -1073,6 +1115,22 @@ static int _lvchange_activate_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_activate_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv)
{
cmd->handles_missing_pvs = 1;
@@ -1089,7 +1147,8 @@ int lvchange_activate_cmd(struct cmd_context *cmd, int argc, char **argv)
if (is_change_activating((activation_change_t)arg_uint_value(cmd, activate_ARG, CHANGE_AY)))
cmd->lockd_vg_enforce_sh = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, _lvchange_activate_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0,
NULL, &_lvchange_activate_check, &_lvchange_activate_single);
}
static int _lvchange_refresh_single(struct cmd_context *cmd,
@@ -1101,15 +1160,40 @@ static int _lvchange_refresh_single(struct cmd_context *cmd,
if (!lv_refresh(cmd, lv))
return_ECMD_FAILED;
/*
* FIXME: In some cases, the lv_refresh() starts polling without
* checking poll arg. Pull that out of lv_refresh.
*/
if (arg_is_set(cmd, poll_ARG) &&
!_lvchange_background_polling(cmd, lv))
return_ECMD_FAILED;
return ECMD_PROCESSED;
}
static int _lvchange_refresh_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
int lvchange_refresh_cmd(struct cmd_context *cmd, int argc, char **argv)
{
cmd->handles_missing_pvs = 1;
cmd->lockd_vg_default_sh = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, _lvchange_refresh_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0,
NULL, &_lvchange_refresh_check, &_lvchange_refresh_single);
}
static int _lvchange_resync_single(struct cmd_context *cmd,
@@ -1122,9 +1206,24 @@ static int _lvchange_resync_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_resync_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
return 1;
return 0;
}
return 1;
}
int lvchange_resync_cmd(struct cmd_context *cmd, int argc, char **argv)
{
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL, _lvchange_resync_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvchange_resync_check, &_lvchange_resync_single);
}
static int _lvchange_syncaction_single(struct cmd_context *cmd,
@@ -1137,9 +1236,24 @@ static int _lvchange_syncaction_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_syncaction_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
return 1;
return 0;
}
return 1;
}
int lvchange_syncaction_cmd(struct cmd_context *cmd, int argc, char **argv)
{
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL, _lvchange_syncaction_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvchange_syncaction_check, &_lvchange_syncaction_single);
}
static int _lvchange_rebuild_single(struct cmd_context *cmd,
@@ -1152,9 +1266,24 @@ static int _lvchange_rebuild_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_rebuild_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
return 1;
return 0;
}
return 1;
}
int lvchange_rebuild_cmd(struct cmd_context *cmd, int argc, char **argv)
{
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL, _lvchange_rebuild_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvchange_rebuild_check, &_lvchange_rebuild_single);
}
static int _lvchange_monitor_poll_single(struct cmd_context *cmd,
@@ -1172,10 +1301,25 @@ static int _lvchange_monitor_poll_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_monitor_poll_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
return 1;
return 0;
}
return 1;
}
int lvchange_monitor_poll_cmd(struct cmd_context *cmd, int argc, char **argv)
{
cmd->handles_missing_pvs = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, _lvchange_monitor_poll_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0,
NULL, &_lvchange_monitor_poll_check, &_lvchange_monitor_poll_single);
}
static int _lvchange_persistent_single(struct cmd_context *cmd,
@@ -1188,10 +1332,27 @@ static int _lvchange_persistent_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvchange_persistent_check(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_named_arg)
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
int lvchange_persistent_cmd(struct cmd_context *cmd, int argc, char **argv)
{
cmd->handles_missing_pvs = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL, _lvchange_persistent_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvchange_persistent_check, &_lvchange_persistent_single);
}
int lvchange(struct cmd_context *cmd, int argc, char **argv)

View File

@@ -41,20 +41,17 @@ typedef enum {
/* Merge:
* If merge_snapshot is also set:
* merge thin snapshot LV into its origin
* merge old snapshot COW into its origin
* or if merge_mirror is also set
* merge LV previously split from mirror back into mirror
*/
CONV_MERGE = 1,
/* Split:
* For a snapshot, split it apart into COW and origin for future recombination
* For a cached LV, split it apart into the cached LV and its pool
* For a mirrored or raid LV, split mirror into two mirrors, optionally tracking
* future changes to the main mirror to allow future recombination.
*/
CONV_SPLIT = 2,
CONV_SPLIT_SNAPSHOT = 3,
CONV_SPLIT_CACHE = 4,
CONV_SPLIT_MIRRORS = 5,
@@ -64,9 +61,6 @@ typedef enum {
/* Destroy the cache attached to a cached LV */
CONV_UNCACHE = 7,
/* Reconstruct a snapshot from its origin and COW */
CONV_SNAPSHOT = 8,
/* Convert normal LV into one in a thin pool */
CONV_THIN = 11,
@@ -78,19 +72,16 @@ struct lvconvert_params {
/* Exactly one of these 12 command categories is determined */
int merge; /* 1 */
int split; /* 2 */
int splitsnapshot; /* 3 */
int splitcache; /* 4 */
int keep_mimages; /* 5 */ /* --splitmirrors */
int cache; /* 6 */
int uncache; /* 7 */
int snapshot; /* 8 */
int thin; /* 11 */
/* other */ /* 12 */
/* FIXME Eliminate all cases where more than one of the above are set then use conv_type instead */
conversion_type_t conv_type;
int merge_snapshot; /* CONV_MERGE is set */
int merge_mirror; /* CONV_MERGE is set */
int track_changes; /* CONV_SPLIT_MIRRORS is set */
@@ -202,13 +193,6 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
return 0;
}
if (!strstr((*pargv)[0], "_rimage_")) { /* Snapshot */
lp->type_str = SEG_TYPE_NAME_SNAPSHOT;
lp->merge_snapshot = 1;
return 1;
}
/* Mirror */
lp->merge_mirror = 1;
}
@@ -223,11 +207,6 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
"the external origin.");
return 0;
}
if (lp->snapshot) {
log_error("Please specify a logical volume to act as "
"the snapshot exception store.");
return 0;
}
if (lp->split) {
log_error("Logical volume for split is missing.");
return 0;
@@ -296,14 +275,6 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
return_0;
if (*pargc) {
if (lp->snapshot) {
log_error("Too many arguments provided for snapshots.");
return 0;
}
if (lp->splitsnapshot) {
log_error("Too many arguments provided with --splitsnapshot.");
return 0;
}
if (lp->splitcache) {
log_error("Too many arguments provided with --splitcache.");
return 0;
@@ -328,12 +299,6 @@ static int _lvconvert_name_params(struct lvconvert_params *lp,
return 1;
}
/* -s/--snapshot and --type snapshot are synonyms */
static int _snapshot_type_requested(struct cmd_context *cmd, const char *type_str)
{
return (arg_is_set(cmd, snapshot_ARG) || !strcmp(type_str, SEG_TYPE_NAME_SNAPSHOT));
}
static int _raid0_type_requested(const char *type_str)
{
return (!strcmp(type_str, SEG_TYPE_NAME_RAID0) || !strcmp(type_str, SEG_TYPE_NAME_RAID0_META));
@@ -531,14 +496,6 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
return_0;
lp->splitcache = 1;
_set_conv_type(lp, CONV_SPLIT_CACHE);
} else if (arg_is_set(cmd, splitsnapshot_ARG)) {
if (arg_outside_list_is_set(cmd, "cannot be used with --splitsnapshot",
splitsnapshot_ARG,
force_ARG, noudevsync_ARG, test_ARG,
-1))
return_0;
lp->splitsnapshot = 1;
_set_conv_type(lp, CONV_SPLIT_SNAPSHOT);
} else if (arg_is_set(cmd, uncache_ARG)) {
if (arg_outside_list_is_set(cmd, "cannot be used with --uncache",
uncache_ARG,
@@ -585,15 +542,6 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
if (!_read_pool_params(cmd, &argc, &argv, lp))
return_0;
if (_snapshot_type_requested(cmd, lp->type_str)) {
if (lp->merge) {
log_error("--snapshot and --merge are mutually exclusive.");
return 0;
}
lp->snapshot = 1;
_set_conv_type(lp, CONV_SNAPSHOT);
}
if (lp->split) {
lp->lv_split_name = arg_str_value(cmd, name_ARG, NULL);
@@ -632,17 +580,17 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
}
/* If no other case was identified, then use of --stripes means --type striped */
if (!arg_is_set(cmd, type_ARG) && !*lp->type_str && !lp->merge && !lp->splitsnapshot &&
!lp->splitcache && !lp->split && !lp->snapshot && !lp->uncache && !lp->cache && !lp->thin &&
if (!arg_is_set(cmd, type_ARG) && !*lp->type_str && !lp->merge &&
!lp->splitcache && !lp->split && !lp->uncache && !lp->cache && !lp->thin &&
!lp->mirrorlog && !lp->corelog &&
(arg_is_set(cmd, stripes_long_ARG) || arg_is_set(cmd, stripesize_ARG)))
lp->type_str = SEG_TYPE_NAME_STRIPED;
if ((_snapshot_type_requested(cmd, lp->type_str) || lp->merge) &&
if (lp->merge &&
(lp->mirrorlog || _mirror_or_raid_type_requested(cmd, lp->type_str) ||
arg_is_set(cmd, thinpool_ARG) || _raid0_type_requested(lp->type_str) ||
_striped_type_requested(lp->type_str))) {
log_error("--snapshot/--type snapshot or --merge argument "
log_error("--merge argument "
"cannot be mixed with --mirrors/--type mirror/--type raid*/--stripes/--type striped/--type linear, "
"--mirrorlog, --repair or --thinpool.");
return 0;
@@ -666,8 +614,8 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
/* We should have caught all these cases already. */
if (lp->merge + lp->splitsnapshot + lp->splitcache + lp->split + lp->uncache +
lp->cache + lp->thin + lp->keep_mimages + lp->snapshot > 1) {
if (lp->merge + lp->splitcache + lp->split + lp->uncache +
lp->cache + lp->thin + lp->keep_mimages > 1) {
log_error(INTERNAL_ERROR "Unexpected combination of incompatible options selected.");
return 0;
}
@@ -676,14 +624,12 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
/*
* Final checking of each case:
* lp->merge
* lp->splitsnapshot
* lp->splitcache
* lp->split
* lp->uncache
* lp->cache
* lp->thin
* lp->keep_mimages
* lp->snapshot
* --type mirror|raid lp->mirrorlog lp->corelog
* --type raid0|striped
*/
@@ -692,46 +638,10 @@ static int _read_params(struct cmd_context *cmd, int argc, char **argv,
case CONV_SPLIT:
case CONV_SPLIT_CACHE:
case CONV_SPLIT_MIRRORS:
case CONV_SPLIT_SNAPSHOT: /* Destroy snapshot retaining cow as separate LV */
case CONV_CACHE:
case CONV_UNCACHE:
case CONV_THIN:
break;
case CONV_SNAPSHOT: /* Snapshot creation from pre-existing cow */
if (!argc) {
log_error("Please provide logical volume path for snapshot origin.");
return 0;
}
lp->origin_name = argv[0];
argv++, argc--;
if (arg_is_set(cmd, regionsize_ARG)) {
log_error("--regionsize is only available with mirrors");
return 0;
}
if (arg_is_set(cmd, stripesize_ARG) || arg_is_set(cmd, stripes_long_ARG)) {
log_error("--stripes and --stripesize are only available with striped mirrors");
return 0;
}
if (arg_is_set(cmd, chunksize_ARG) &&
(arg_sign_value(cmd, chunksize_ARG, SIGN_NONE) == SIGN_MINUS)) {
log_error("Negative chunk size is invalid.");
return 0;
}
lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
!is_power_of_2(lp->chunk_size)) {
log_error("Chunk size must be a power of 2 in the "
"range 4K to 512K");
return 0;
}
log_verbose("Setting chunk size to %s.", display_size(cmd, lp->chunk_size));
lp->type_str = SEG_TYPE_NAME_SNAPSHOT;
break;
case CONV_OTHER:
if (_mirror_or_raid_type_requested(cmd, lp->type_str) ||
@@ -2166,12 +2076,25 @@ static int _lvconvert_snapshot(struct cmd_context *cmd,
return_0;
/*
* lv_is_prop checks here cannot be automated by command definition
* rules because they are not done on the LV being processed.
* check_lv_rules() checks cannot be done via command definition
* rules because this LV is not processed by process_each_lv.
*/
if (lv_is_locked(org) || lv_is_pvmove(org)) {
log_error("Unable to convert an LV into a snapshot of a %s LV.",
lv_is_locked(org) ? "locked" : "pvmove");
log_error("Unable to use LV %s as snapshot origin: LV is %s.",
display_lvname(lv), lv_is_locked(org) ? "locked" : "pvmove");
return 0;
}
/*
* check_lv_types() checks cannot be done via command definition
* LV_foo specification because this LV is not processed by process_each_lv.
*/
if (lv_is_cache_type(org) ||
lv_is_thin_type(org) ||
lv_is_mirrored(org) ||
lv_is_cow(org)) {
log_error("Unable to use LV %s as snapshot origin: invald LV type.",
display_lvname(lv));
return 0;
}
@@ -3363,26 +3286,6 @@ static int _lvconvert_cache(struct cmd_context *cmd,
* moved into the _convert_lvtype_operation() functions below.
*/
/*
* Separate a COW snapshot LV from its origin.
* lvconvert --splitsnapshot LV
*/
static int _convert_cow_snapshot_splitsnapshot(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
{
return _lvconvert_splitsnapshot(cmd, lv);
}
/*
* Merge a COW snapshot LV into its origin.
* lvconvert --merge LV
*/
static int _convert_cow_snapshot_merge(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
{
/* return _lvconvert_merge_old_snapshot(cmd, lv, lp); */
}
/*
* Merge a snapshot thin LV into its origin.
* lvconvert --merge LV
@@ -3663,20 +3566,6 @@ static int _convert_raid_merge(struct cmd_context *cmd, struct logical_volume *l
return _lvconvert_raid(lv, lp);
}
/*
* Combine a raid* LV with a snapshot LV that was previously
* split from the raid* LV using --splitsnapshot.
* lvconvert --type snapshot LV SnapshotLV
*
* Alternate syntax:
* lvconvert --snapshot LV SnapshotLV
*/
static int _convert_raid_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
{
return _lvconvert_snapshot(cmd, lv, lp->origin_name);
}
/*
* Convert a raid* LV to a thin LV with an external origin.
* lvconvert --type thin LV
@@ -3802,20 +3691,6 @@ static int _convert_striped_merge(struct cmd_context *cmd, struct logical_volume
return _lvconvert_raid(lv, lp);
}
/*
* Combine a linear/striped LV with a snapshot LV that was previously
* split from the linear/striped LV using --splitsnapshot.
* lvconvert --type snapshot LV SnapshotLV
*
* Alternate syntax:
* lvconvert --snapshot LV SnapshotLV
*/
static int _convert_striped_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
{
return _lvconvert_snapshot(cmd, lv, lp->origin_name);
}
/*
* Convert a striped/linear LV to a thin LV with an external origin.
* lvconvert --type thin LV
@@ -3930,22 +3805,6 @@ static int _convert_striped_raid(struct cmd_context *cmd, struct logical_volume
*
* _convert_<lvtype>
*/
static int _convert_cow_snapshot(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
{
if (lp->splitsnapshot)
return _convert_cow_snapshot_splitsnapshot(cmd, lv, lp);
/* FIXME: add --merge-snapshot to make this distinct from --merge-mirror. */
if (lp->merge)
return _convert_cow_snapshot_merge(cmd, lv, lp);
log_error("Operation not permitted on COW snapshot LV %s.", display_lvname(lv));
log_error("Operations permitted on a COW snapshot LV are:\n"
" --splitsnapshot\n"
" --merge\n");
return 0;
}
static int _convert_thin_volume(struct cmd_context *cmd, struct logical_volume *lv,
struct lvconvert_params *lp)
@@ -4147,9 +4006,6 @@ static int _convert_raid(struct cmd_context *cmd, struct logical_volume *lv,
if (lp->merge)
return _convert_raid_merge(cmd, lv, lp);
if (!strcmp(lp->type_str, SEG_TYPE_NAME_SNAPSHOT) || arg_is_set(cmd, snapshot_ARG))
return _convert_raid_snapshot(cmd, lv, lp);
if (lp->thin)
return _convert_raid_thin(cmd, lv, lp);
@@ -4181,9 +4037,6 @@ static int _convert_striped(struct cmd_context *cmd, struct logical_volume *lv,
if (lp->merge)
return _convert_striped_merge(cmd, lv, lp);
if (lp->snapshot || !strcmp(lp->type_str, SEG_TYPE_NAME_SNAPSHOT))
return _convert_striped_snapshot(cmd, lv, lp);
if (lp->thin)
return _convert_striped_thin(cmd, lv, lp);
@@ -4322,18 +4175,10 @@ static int _lvconvert(struct cmd_context *cmd, struct logical_volume *lv,
lp->stripes = 0;
}
if (lp->snapshot)
lp->zero = (lp->segtype->flags & SEG_CANNOT_BE_ZEROED) ? 0 : arg_int_value(cmd, zero_ARG, 1);
/*
* Each LV type that can be converted.
* (The existing type of the LV, not a requested type.)
*/
if (lv_is_cow(lv)) {
ret = _convert_cow_snapshot(cmd, lv, lp);
goto out;
}
if (lv_is_thin_volume(lv)) {
ret = _convert_thin_volume(cmd, lv, lp);
goto out;
@@ -4490,7 +4335,7 @@ int lvconvert(struct cmd_context * cmd, int argc, char **argv)
if (lp.merge) {
ret = process_each_lv(cmd, argc, argv, NULL, NULL,
READ_FOR_UPDATE, handle, &_lvconvert_merge_single);
READ_FOR_UPDATE, handle, NULL, &_lvconvert_merge_single);
} else {
int saved_ignore_suspended_devices = ignore_suspended_devices();
@@ -4500,7 +4345,7 @@ int lvconvert(struct cmd_context * cmd, int argc, char **argv)
}
ret = process_each_lv(cmd, 0, NULL, lp.vg_name, lp.lv_name,
READ_FOR_UPDATE, handle, &_lvconvert_single);
READ_FOR_UPDATE, handle, NULL, &_lvconvert_single);
init_ignore_suspended_devices(saved_ignore_suspended_devices);
}
@@ -4546,6 +4391,36 @@ out:
*/
/*
* FIXME: it's very unlikely that the same !visible exceptions apply to every
* lvconvert command. Add specific !visible exceptions in command-specific
* check functions.
*/
static int _lvconvert_generic_check(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
if (lv_is_cache_pool_metadata(lv) ||
lv_is_cache_pool_data(lv) ||
lv_is_thin_pool_metadata(lv) ||
lv_is_thin_pool_data(lv) ||
lv_is_used_cache_pool(lv) ||
lv_is_mirrored(lv) ||
lv_is_raid(lv)) {
return 1;
}
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
/*
* Data/results accumulated during processing.
*/
@@ -4750,8 +4625,8 @@ int lvconvert_repair_pvs_or_thinpool_cmd(struct cmd_context *cmd, int argc, char
cmd->handles_missing_pvs = 1;
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle,
&_lvconvert_repair_pvs_or_thinpool_single);
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
handle, &_lvconvert_generic_check, &_lvconvert_repair_pvs_or_thinpool_single);
init_ignore_suspended_devices(saved_ignore_suspended_devices);
@@ -4827,8 +4702,8 @@ int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &lr;
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle,
&_lvconvert_replace_pv_single);
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
handle, &_lvconvert_generic_check, &_lvconvert_replace_pv_single);
destroy_processing_handle(cmd, handle);
@@ -4857,7 +4732,7 @@ static int _lvconvert_merge_snapshot_single(struct cmd_context *cmd,
return_ECMD_FAILED;
if (lv_to_poll) {
if (!(idl = _convert_poll_id_list_create(cmd, lv)))
if (!(idl = _convert_poll_id_list_create(cmd, lv_to_poll)))
return_ECMD_FAILED;
dm_list_add(&lr->poll_idls, &idl->list);
lr->need_polling = 1;
@@ -4866,6 +4741,20 @@ static int _lvconvert_merge_snapshot_single(struct cmd_context *cmd,
return ECMD_PROCESSED;
}
static int _lvconvert_merge_snapshot_check(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg)
{
if (!lv_is_visible(lv)) {
log_error("Operation not permitted (%s %d) on hidden LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lv));
return 0;
}
return 1;
}
int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
@@ -4882,13 +4771,13 @@ int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv)
handle->custom_handle = &lr;
ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, handle,
_lvconvert_merge_snapshot_single);
ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
handle, &_lvconvert_merge_snapshot_check, &_lvconvert_merge_snapshot_single);
if (lr.need_polling) {
dm_list_iterate_items(idl, &lr.poll_idls) {
poll_ret = _lvconvert_poll_by_id(cmd, idl->id,
arg_is_set(cmd, background_ARG), 0, 0);
arg_is_set(cmd, background_ARG), 1, 0);
if (poll_ret > ret)
ret = poll_ret;
}
@@ -4906,11 +4795,6 @@ int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv)
* lvconvert_split_cow_snapshot
*/
/*
* TODO: ensure that LV_snapshot implies that the LV cannot be thinpool,
* mirror_type, raid_type, thin_type
*/
static int _lvconvert_split_snapshot_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle)
@@ -4923,25 +4807,61 @@ static int _lvconvert_split_snapshot_single(struct cmd_context *cmd,
int lvconvert_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv)
{
return process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, NULL,
_lvconvert_split_snapshot_single);
return process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvconvert_generic_check, &_lvconvert_split_snapshot_single);
}
/*
* Combine an origin LV with a snapshot LV (cow) that was previously split from
* the origin using --splitsnapshot.
*/
/*
* TODO: ensure that LV_snapshot implies that the LV cannot be other types
* that were checked for: lv_is_cache_type lv_is_mirrored lv_is_pool lv_is_thick_origin
* Combine two LVs that were once an origin/cow pair of LVs, were then
* separated with --splitsnapshot, and now with this command are combined again
* into the origin/cow pair.
*
* This is an obscure command that has little to no real uses.
*
* The command has unusual handling of position args. The first position arg
* will become the origin LV, and is not processed by process_each_lv. The
* second position arg will become the cow LV and is processed by
* process_each_lv.
*
* The single function can grab the origin LV from position_argv[0].
*
* begin with an ordinary LV foo:
* lvcreate -n foo -L 1 vg
*
* create a cow snapshot of foo named foosnap:
* lvcreate -s -L 1 -n foosnap vg/foo
*
* now, foo is an "origin LV" and foosnap is a "cow LV"
* (foosnap matches LV_snapshot aka lv_is_cow)
*
* split the two LVs apart:
* lvconvert --splitsnapshot vg/foosnap
*
* now, foo is *not* an origin LV and foosnap is *not* a cow LV
* (foosnap does not match LV_snapshot)
*
* now, combine the two LVs again:
* lvconvert --snapshot vg/foo vg/foosnap
*
* after this, foosnap will match LV_snapshot again.
*
* FIXME: when splitsnapshot is run, the previous cow LV should be
* flagged in the metadata somehow, and then that flag should be
* required here. As it is now, the first and second args
* (origin and cow) can be swapped and nothing catches it.
*/
static int _lvconvert_combine_split_snapshot_single(struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle)
{
if (!_lvconvert_snapshot(cmd, lv, cmd->position_argv[0]))
const char *origin_name = cmd->position_argv[0];
/* If origin_name includes VG name, the VG name is removed. */
if (!validate_lvname_param(cmd, &lv->vg->name, &origin_name))
return_ECMD_FAILED;
if (!_lvconvert_snapshot(cmd, lv, origin_name))
return_ECMD_FAILED;
return ECMD_PROCESSED;
@@ -4949,20 +4869,7 @@ static int _lvconvert_combine_split_snapshot_single(struct cmd_context *cmd,
int lvconvert_combine_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv)
{
const char *origin_name;
origin_name = cmd->position_argv[0];
/* TODO: validate origin_name, see validate_restricted_lvname_param */
/*
* The command has unusual handling of position args. The first
* position arg is the origin name, and that LV is not processed by
* process_each_lv. The second position arg is the snapshot LV that's
* processed by process_each_lv. The single function can grab the
* origin LV from position_argv[0].
*/
return process_each_lv(cmd, 1, cmd->position_argv + 1, NULL, NULL, READ_FOR_UPDATE, NULL,
_lvconvert_combine_split_snapshot_single);
return process_each_lv(cmd, 1, cmd->position_argv + 1, NULL, NULL, READ_FOR_UPDATE,
NULL, &_lvconvert_generic_check, &_lvconvert_combine_split_snapshot_single);
}

View File

@@ -58,5 +58,5 @@ int lvdisplay(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &_lvdisplay_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &_lvdisplay_single);
}

View File

@@ -27,5 +27,5 @@ int lvremove(struct cmd_context *cmd, int argc, char **argv)
cmd->include_historical_lvs = 1;
return process_each_lv(cmd, argc, argv, NULL, NULL, READ_FOR_UPDATE, NULL,
&lvremove_single);
NULL, &lvremove_single);
}

View File

@@ -119,5 +119,5 @@ int lvscan(struct cmd_context *cmd, int argc, char **argv)
*/
}
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &lvscan_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, NULL, &lvscan_single);
}

View File

@@ -517,14 +517,14 @@ static int _report_all_in_vg(struct cmd_context *cmd, struct processing_handle *
r = _vgs_single(cmd, vg->name, vg, handle);
break;
case LVS:
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
do_lv_info && !do_lv_seg_status ? &_lvs_with_info_single :
!do_lv_info && do_lv_seg_status ? &_lvs_with_status_single :
do_lv_info && do_lv_seg_status ? &_lvs_with_info_and_status_single :
&_lvs_single);
break;
case SEGS:
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle,
r = process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, handle, NULL,
do_lv_info && !do_lv_seg_status ? &_lvsegs_with_info_single :
!do_lv_info && do_lv_seg_status ? &_lvsegs_with_status_single :
do_lv_info && do_lv_seg_status ? &_lvsegs_with_info_and_status_single :
@@ -1099,7 +1099,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
if (args->full_report_vg)
r = _report_all_in_vg(cmd, handle, args->full_report_vg, LVS, lv_info_needed, lv_segment_status_needed);
else
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
lv_info_needed && !lv_segment_status_needed ? &_lvs_with_info_single :
!lv_info_needed && lv_segment_status_needed ? &_lvs_with_status_single :
lv_info_needed && lv_segment_status_needed ? &_lvs_with_info_and_status_single :
@@ -1133,7 +1133,7 @@ static int _do_report(struct cmd_context *cmd, struct processing_handle *handle,
if (args->full_report_vg)
r = _report_all_in_vg(cmd, handle, args->full_report_vg, SEGS, lv_info_needed, lv_segment_status_needed);
else
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle,
r = process_each_lv(cmd, args->argc, args->argv, NULL, NULL, 0, handle, NULL,
lv_info_needed && !lv_segment_status_needed ? &_lvsegs_with_info_single :
!lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_status_single :
lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_info_and_status_single :

View File

@@ -2529,6 +2529,10 @@ static int _lv_is_type(struct cmd_context *cmd, struct logical_volume *lv, int l
#endif
case raid10_LVT:
return seg_is_raid10(seg);
case error_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR);
case zero_LVT:
return !strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO);
default:
log_error(INTERNAL_ERROR "unknown lv type value lvt_enum %d", lvt_enum);
}
@@ -2573,7 +2577,11 @@ static int _get_lvt_enum(struct logical_volume *lv)
if (seg_is_raid10(seg))
return raid10_LVT;
log_error(INTERNAL_ERROR "unknown lv type for %s", display_lvname(lv));
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ERROR))
return error_LVT;
if (!strcmp(seg->segtype->name, SEG_TYPE_NAME_ZERO))
return zero_LVT;
return 0;
}
@@ -2669,35 +2677,29 @@ static int _lv_props_match(struct cmd_context *cmd, struct logical_volume *lv, u
return !found_a_mismatch;
}
/*
* If the command definition specifies one required positional
* LV (possibly repeatable), and specifies accepted LV types,
* then verify that the LV being processed matches one of those
* types.
*
* process_each_lv() can only be used for commands that have
* one positional LV arg (optionally repeating, where each is
* processed independently.) It cannot work for commands that
* have different required LVs in designated positions, like
* 'lvrename LV1 LV2', where each LV is not processed
* independently. That means that this LV type check only
* needs to check the lv_type of the first positional arg.
*/
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv)
static int _check_lv_types(struct cmd_context *cmd, struct logical_volume *lv, int pos)
{
int ret = 1;
if ((cmd->command->rp_count == 1) &&
val_bit_is_set(cmd->command->required_pos_args[0].def.val_bits, lv_VAL) &&
cmd->command->required_pos_args[0].def.lvt_bits) {
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[0].def.lvt_bits, NULL, NULL);
if (!ret) {
int lvt_enum = _get_lvt_enum(lv);
struct lv_types *type = get_lv_type(lvt_enum);
log_warn("Operation on LV %s which has invalid type %s.",
display_lvname(lv), type ? type->name : "unknown");
}
if (!pos)
return 1;
if (!cmd->command->required_pos_args[pos-1].def.lvt_bits)
return 1;
if (!val_bit_is_set(cmd->command->required_pos_args[pos-1].def.val_bits, lv_VAL)) {
log_error(INTERNAL_ERROR "Command (%s %d) arg position %d does not permit an LV (%llx)",
cmd->command->command_line_id, cmd->command->command_line_enum,
pos, (unsigned long long)cmd->command->required_pos_args[pos-1].def.val_bits);
return 0;
}
ret = _lv_types_match(cmd, lv, cmd->command->required_pos_args[pos-1].def.lvt_bits, NULL, NULL);
if (!ret) {
int lvt_enum = _get_lvt_enum(lv);
struct lv_types *type = get_lv_type(lvt_enum);
log_warn("Operation on LV %s which has invalid type %s.",
display_lvname(lv), type ? type->name : "unknown");
}
return ret;
@@ -2861,10 +2863,64 @@ static int _check_lv_rules(struct cmd_context *cmd, struct logical_volume *lv)
return ret;
}
/*
* Return which arg position the given LV is at,
* where 1 represents the first position arg.
* When the first position arg is repeatable,
* return 1 for all.
*
* Return 0 when the command has no required
* position args. (optional position args are
* not considered.)
*/
static int _find_lv_arg_position(struct cmd_context *cmd, struct logical_volume *lv)
{
const char *sep, *lvname;
int i;
if (cmd->command->rp_count == 0)
return 0;
if (cmd->command->rp_count == 1)
return 1;
for (i = 0; i < cmd->position_argc; i++) {
if (i == cmd->command->rp_count)
break;
if (!val_bit_is_set(cmd->command->required_pos_args[i].def.val_bits, lv_VAL))
continue;
if ((sep = strstr(cmd->position_argv[i], "/")))
lvname = sep + 1;
else
lvname = cmd->position_argv[i];
if (!strcmp(lvname, lv->name))
return i + 1;
}
/*
* If the last position arg is an LV and this
* arg is beyond that position, then the last
* LV position arg is repeatable, so return
* that position.
*/
if (i == cmd->command->rp_count) {
int last_pos = cmd->command->rp_count;
if (val_bit_is_set(cmd->command->required_pos_args[last_pos-1].def.val_bits, lv_VAL))
return last_pos;
}
return 0;
}
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list *arg_lvnames, const struct dm_list *tags_in,
int stop_on_error,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -2874,10 +2930,12 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
int ret = 0;
int whole_selected = 0;
int handle_supplied = handle != NULL;
int lv_is_named_arg;
unsigned process_lv;
unsigned process_all = 0;
unsigned tags_supplied = 0;
unsigned lvargs_supplied = 0;
int lv_arg_pos;
struct lv_list *lvl;
struct dm_str_list *sl;
struct dm_list final_lvs;
@@ -3035,42 +3093,40 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
if (lv_is_removed(lvl->lv))
continue;
lv_is_named_arg = str_list_match_item(&found_arg_lvnames, lvl->lv->name);
lv_arg_pos = _find_lv_arg_position(cmd, lvl->lv);
/*
* The command definition may include restrictions on the
* types and properties of LVs that can be processed.
*/
if (!_check_lv_types(cmd, lvl->lv)) {
if (!_check_lv_types(cmd, lvl->lv, lv_arg_pos)) {
/* FIXME: include this result in report log? */
/* FIXME: avoid duplicating message for each level */
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name)) {
if (lv_is_named_arg) {
log_error("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
ret_max = ECMD_FAILED;
} else {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
}
continue;
}
if (!_check_lv_rules(cmd, lvl->lv)) {
/* FIXME: include this result in report log? */
/* FIXME: avoid duplicating message for each level */
if (str_list_match_item(&found_arg_lvnames, lvl->lv->name)) {
if (lv_is_named_arg) {
log_error("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
ret_max = ECMD_FAILED;
} else {
log_warn("Operation not permitted (%s %d) on LV %s.",
cmd->command->command_line_id, cmd->command->command_line_enum,
display_lvname(lvl->lv));
}
}
continue;
}
if (check_single_lv && !check_single_lv(cmd, lvl->lv, handle, lv_is_named_arg)) {
if (lv_is_named_arg)
ret_max = ECMD_FAILED;
continue;
}
@@ -3310,6 +3366,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
struct dm_list *arg_lvnames,
struct dm_list *arg_tags,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -3402,7 +3459,7 @@ static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flag
goto endvg;
ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
handle, process_single_lv);
handle, check_single_lv, process_single_lv);
if (ret != ECMD_PROCESSED)
stack;
report_log_ret_code(ret);
@@ -3433,6 +3490,7 @@ int process_each_lv(struct cmd_context *cmd,
const char *one_vgname, const char *one_lvname,
uint32_t read_flags,
struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv)
{
log_report_t saved_log_report_state = log_get_report_state();
@@ -3548,7 +3606,7 @@ int process_each_lv(struct cmd_context *cmd,
_choose_vgs_to_process(cmd, &arg_vgnames, &vgnameids_on_system, &vgnameids_to_process);
ret = _process_lv_vgnameid_list(cmd, read_flags, &vgnameids_to_process, &arg_vgnames, &arg_lvnames,
&arg_tags, handle, process_single_lv);
&arg_tags, handle, check_single_lv, process_single_lv);
if (ret > ret_max)
ret_max = ret;

View File

@@ -98,6 +98,18 @@ typedef int (*process_single_pvseg_fn_t) (struct cmd_context * cmd,
struct pv_segment * pvseg,
struct processing_handle *handle);
/*
* Called prior to process_single_lv() to decide if the LV should be
* processed. If this returns 0, the LV is not processed.
*
* This can evaluate the combination of command definition and
* the LV object to decide if the combination is allowed.
*/
typedef int (*check_single_lv_fn_t) (struct cmd_context *cmd,
struct logical_volume *lv,
struct processing_handle *handle,
int lv_is_named_arg);
int process_each_vg(struct cmd_context *cmd,
int argc, char **argv,
const char *one_vgname,
@@ -125,6 +137,7 @@ int process_each_segment_in_pv(struct cmd_context *cmd,
int process_each_lv(struct cmd_context *cmd, int argc, char **argv,
const char *one_vgname, const char *one_lvname,
uint32_t flags, struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv);
@@ -141,6 +154,7 @@ int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
struct dm_list *arg_lvnames, const struct dm_list *tagsl,
int stop_on_error, struct processing_handle *handle,
check_single_lv_fn_t check_single_lv,
process_single_lv_fn_t process_single_lv);
struct processing_handle *init_processing_handle(struct cmd_context *cmd, struct processing_handle *parent_handle);

View File

@@ -38,7 +38,7 @@ static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name,
vgdisplay_extents(vg);
process_each_lv_in_vg(cmd, vg, NULL, NULL, 0, NULL,
(process_single_lv_fn_t)lvdisplay_full);
NULL, (process_single_lv_fn_t)lvdisplay_full);
log_print("--- Physical volumes ---");
process_each_pv_in_vg(cmd, vg, NULL,

View File

@@ -33,5 +33,5 @@ int vgmknodes(struct cmd_context *cmd, int argc, char **argv)
if (!lv_mknodes(cmd, NULL))
return_ECMD_FAILED;
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, &_vgmknodes_single);
return process_each_lv(cmd, argc, argv, NULL, NULL, LCK_VG_READ, NULL, NULL, &_vgmknodes_single);
}

View File

@@ -62,7 +62,7 @@ static int vgremove_single(struct cmd_context *cmd, const char *vg_name,
}
if ((ret = process_each_lv_in_vg(cmd, vg, NULL, NULL, 1, &void_handle,
(process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
NULL, (process_single_lv_fn_t)lvremove_single)) != ECMD_PROCESSED) {
stack;
return ret;
}