1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/tools/lvresize.c
Zdenek Kabelac bebbb1e66a command: use command_id_to_enum
Instead of storing command_id as string, direcly
translate string to enum index and use 'command_enum()'
to get string when needed for printing.

This way we can easily detect error in the structure
while parsing it - and we can later avoid separate
'translation' loop.
2024-05-13 02:15:55 +02:00

425 lines
12 KiB
C

/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-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 "tools.h"
static int _lvresize_params(struct cmd_context *cmd, struct lvresize_params *lp)
{
const char *type_str = arg_str_value(cmd, type_ARG, NULL);
int only_linear = 0;
int set_fsopt = 0;
int set_extents_and_size = 0;
memset(lp, 0, sizeof(struct lvresize_params));
switch (cmd->command->command_enum) {
case lvextend_policy_CMD:
lp->resize = LV_EXTEND;
lp->size = 0;
lp->extents = 0;
lp->percent = PERCENT_LV;
lp->sign = SIGN_PLUS;
lp->poolmetadata_size = 0;
lp->use_policies = 1;
break;
case lvextend_pool_metadata_CMD:
case lvresize_pool_metadata_CMD:
lp->resize = LV_EXTEND;
lp->size = 0;
lp->extents = 0;
lp->percent = PERCENT_NONE;
lp->sign = SIGN_NONE;
lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0);
lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
break;
case lvextend_pv_CMD:
case lvresize_pv_CMD:
lp->resize = LV_EXTEND;
lp->size = 0;
lp->extents = 0;
lp->percent_value = 100;
lp->percent = PERCENT_PVS;
lp->sign = SIGN_PLUS;
lp->poolmetadata_size = 0;
set_fsopt = 1;
break;
case lvextend_size_CMD:
lp->resize = LV_EXTEND;
if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
set_extents_and_size = 1;
set_fsopt = 1;
break;
case lvreduce_size_CMD:
lp->resize = LV_REDUCE;
lp->poolmetadata_size = 0;
set_extents_and_size = 1;
set_fsopt = 1;
break;
case lvresize_size_CMD:
lp->resize = LV_ANY;
lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0);
if ((lp->poolmetadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG, 0)))
lp->poolmetadata_sign = arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE);
set_extents_and_size = 1;
set_fsopt = 1;
break;
default:
log_error(INTERNAL_ERROR "unknown lvresize type");
return 0;
};
if (set_fsopt) {
const char *str;
if (arg_is_set(cmd, resizefs_ARG) && arg_is_set(cmd, fs_ARG)) {
log_error("Options --fs and --resizefs cannot be used together.");
log_error("--resizefs is equivalent to --fs resize.");
return 0;
}
#ifdef HAVE_BLKID_SUBLKS_FSINFO
/*
* When the libblkid fs info feature is available, use the
* the newer fs resizing capabability unless the older
* fsadm-based resizing is requested with --fs resize_fsadm.
*/
if ((str = arg_str_value(cmd, fs_ARG, NULL))) {
if (!strcmp(str, "checksize") ||
!strcmp(str, "resize") ||
!strcmp(str, "resize_fsadm")) {
strncpy(lp->fsopt, str, sizeof(lp->fsopt)-1);
} else if (!strcmp(str, "ignore")) {
lp->fsopt[0] = '\0';
} else {
log_error("Unknown --fs value.");
return 0;
}
lp->user_set_fs = 1;
} else if (arg_is_set(cmd, resizefs_ARG)) {
/* --resizefs alone equates to --fs resize */
strncpy(lp->fsopt, "resize", sizeof(lp->fsopt)-1);
lp->user_set_fs = 1;
} else {
/*
* Use checksize when no fs option is specified.
* checksize with extend does nothing: the LV
* is extended and any fs is ignored.
* checksize with reduce checks for an fs that
* needs reducing: the LV is reduced only if the
* fs does not need to be reduced (or no fs.)
*/
strncpy(lp->fsopt, "checksize", sizeof(lp->fsopt)-1);
}
#else
/*
* When the libblkid fs info feature is not available we can only
* use fsadm, so --resizefs, --fs resize, --fs resize_fsadm
* all translate to resize_fsadm.
*/
if ((str = arg_str_value(cmd, fs_ARG, NULL))) {
if (!strcmp(str, "resize")) {
log_warn("Using fsadm for file system handling (resize_fsadm).");
strcpy(lp->fsopt, "resize_fsadm");
} else if (!strcmp(str, "resize_fsadm")) {
strcpy(lp->fsopt, "resize_fsadm");
} else if (!strcmp(str, "ignore")) {
log_warn("Ignoring unsupported --fs ignore with fsadm resizing.");
} else {
log_error("Unknown --fs value.");
return 0;
}
} else if (arg_is_set(cmd, resizefs_ARG)) {
/* --resizefs alone equates to --fs resize_fsadm */
strcpy(lp->fsopt, "resize_fsadm");
}
#endif
if (lp->fsopt[0])
lp->nofsck = arg_is_set(cmd, nofsck_ARG);
if (!strcmp(lp->fsopt, "resize_fsadm") && arg_is_set(cmd, fsmode_ARG)) {
log_error("The --fsmode option does not apply to resize_fsadm.");
return 0;
}
if ((str = arg_str_value(cmd, fsmode_ARG, NULL))) {
if (!strcmp(str, "nochange") ||
!strcmp(str, "offline") ||
!strcmp(str, "manage")) {
strncpy(lp->fsmode, str, sizeof(lp->fsmode)-1);
lp->user_set_fsmode = 1;
} else {
log_error("Unknown --fsmode value.");
return 0;
}
} else {
/* Use manage when no fsmode option is specified. */
strncpy(lp->fsmode, "manage", sizeof(lp->fsmode)-1);
}
}
if (set_extents_and_size) {
if ((lp->extents = arg_uint_value(cmd, extents_ARG, 0))) {
lp->sign = arg_sign_value(cmd, extents_ARG, SIGN_NONE);
lp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
}
if ((lp->size = arg_uint64_value(cmd, size_ARG, 0))) {
lp->sign = arg_sign_value(cmd, size_ARG, SIGN_NONE);
lp->percent = PERCENT_NONE;
}
if (lp->size && lp->extents) {
log_error("Please specify either size or extents but not both.");
return 0;
}
}
lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, 0);
lp->yes = arg_is_set(cmd, yes_ARG);
lp->force = arg_is_set(cmd, force_ARG),
lp->nosync = arg_is_set(cmd, nosync_ARG);
lp->lockopt = arg_str_value(cmd, lockopt_ARG, NULL);
if (type_str) {
if (!strcmp(type_str, "linear")) {
type_str = "striped";
only_linear = 1; /* User requested linear only target */
}
if (!(lp->segtype = get_segtype_from_string(cmd, type_str)))
return_0;
}
if ((lp->resize == LV_REDUCE) &&
(type_str ||
arg_is_set(cmd, mirrors_ARG) ||
arg_is_set(cmd, stripes_ARG) ||
arg_is_set(cmd, stripesize_ARG))) {
/* should be obvious since reduce doesn't alloc space. */
log_print_unless_silent("Ignoring type, stripes, stripesize and mirrors "
"arguments when reducing.");
goto out;
}
if (arg_is_set(cmd, mirrors_ARG)) {
if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) != SIGN_NONE) {
log_error("Mirrors argument may not be signed.");
return 0;
}
if ((lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0)))
lp->mirrors++;
}
if ((lp->stripes = arg_uint_value(cmd, stripes_ARG, 0)) &&
(arg_sign_value(cmd, stripes_ARG, SIGN_NONE) == SIGN_MINUS)) {
log_error("Stripes argument may not be negative.");
return 0;
}
if (only_linear && lp->stripes > 1) {
log_error("Cannot use stripes with linear type.");
return 0;
}
if ((lp->stripe_size = arg_uint64_value(cmd, stripesize_ARG, 0)) &&
(arg_sign_value(cmd, stripesize_ARG, SIGN_NONE) == SIGN_MINUS)) {
log_error("Stripesize may not be negative.");
return 0;
}
out:
return 1;
}
/*
* lvextend --use-policies is usually called by dmeventd, as a method of
* "auto extending" an LV as it's used. It checks how full a snapshot cow or
* thin pool is, and extends it if it's too full, based on threshold settings
* in lvm.conf for when to auto extend it.
*
* The extension of a thin pool LV can involve extending either the data sub
* LV, the metadata sub LV, or both, so there may be two LVs extended here.
*/
static int _lv_extend_policy(struct cmd_context *cmd, struct logical_volume *lv,
struct lvresize_params *lp, int *skipped)
{
uint32_t percent_main = 0;
uint32_t percent_meta = 0;
int is_active;
if (lv_is_cow(lv))
is_active = lv_is_active(lv);
else if (lv_is_thin_pool(lv) || lv_is_vdo_pool(lv))
/* check for -layer active LV */
is_active = lv_info(lv->vg->cmd, lv, 1, NULL, 0, 0);
else {
log_error("lvextend policy is supported only for snapshot, thin pool and vdo pool volumes.");
return 0;
}
if (!is_active) {
log_error("lvextend using policy requires the volume to be active.");
return 0;
}
/*
* Calculate the percent of extents to extend the LV based on current
* usage info from the kernel and policy settings from lvm.conf, e.g.
* autoextend_threshold, autoextend_percent. For thin pools, both the
* thin pool data LV and thin pool metadata LV may need to be extended.
* In this case, percent_main is the amount to extend the data LV, and
* percent_meta is the amount to extend the metadata LV.
*/
if (!lv_extend_policy_calculate_percent(lv, &percent_main, &percent_meta))
return_0;
if (!percent_main && !percent_meta) {
log_debug("lvextend policy not needed.");
*skipped = 1;
return 1;
}
*skipped = 0;
lp->policy_percent_main = percent_main;
lp->policy_percent_meta = percent_meta;
return lv_resize(cmd, lv, lp);
}
static int _lvextend_policy_single(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle)
{
struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
int skipped = 0;
if (cmd->position_argc > 1) {
/* First pos arg is required LV, remaining are optional PVs. */
if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0)))
return_ECMD_FAILED;
} else
lp->pvh = &lv->vg->pvs;
if (!_lv_extend_policy(cmd, lv, lp, &skipped))
return ECMD_FAILED;
if (!skipped)
log_print_unless_silent("Logical volume %s successfully resized.", display_lvname(lv));
return ECMD_PROCESSED;
}
static int _lvresize_single(struct cmd_context *cmd, struct logical_volume *lv,
struct processing_handle *handle)
{
struct lvresize_params *lp = (struct lvresize_params *) handle->custom_handle;
int ret;
if (cmd->position_argc > 1) {
/* First pos arg is required LV, remaining are optional PVs. */
if (!(lp->pvh = create_pv_list(cmd->mem, lv->vg, cmd->position_argc - 1, cmd->position_argv + 1, 0)))
return_ECMD_FAILED;
} else
lp->pvh = &lv->vg->pvs;
ret = lv_resize(cmd, lv, lp);
if (ret || lp->extend_fs_error)
log_print_unless_silent("Logical volume %s successfully resized.",
display_lvname(lv));
if (!ret)
return ECMD_FAILED;
return ECMD_PROCESSED;
}
int lvextend_policy_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
struct lvresize_params lp;
int ret;
if (!_lvresize_params(cmd, &lp))
return EINVALID_CMD_LINE;
if (!(handle = init_processing_handle(cmd, NULL)))
return ECMD_FAILED;
handle->custom_handle = &lp;
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
handle, NULL, &_lvextend_policy_single);
destroy_processing_handle(cmd, handle);
return ret;
}
int lvresize_cmd(struct cmd_context *cmd, int argc, char **argv)
{
struct processing_handle *handle;
struct lvresize_params lp;
int retries = 0;
int ret;
if (!_lvresize_params(cmd, &lp))
return EINVALID_CMD_LINE;
if (!(handle = init_processing_handle(cmd, NULL)))
return ECMD_FAILED;
handle->custom_handle = &lp;
retry:
ret = process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE,
handle, NULL, &_lvresize_single);
/*
* The VG can be changed by another command while it is unlocked
* during fs resize. The fs steps likely succeeded, and this
* retry will likely find that no more fs steps are needed, and
* will resize the LV directly.
*/
if (lp.vg_changed_error && !retries) {
lp.vg_changed_error = 0;
retries = 1;
goto retry;
} else if (lp.vg_changed_error && retries) {
log_error("VG changed during file system resize, LV not resized.");
ret = ECMD_FAILED;
}
destroy_processing_handle(cmd, handle);
if (lp.lockd_lv_refresh_path && !lockd_lv_refresh(cmd, &lp))
ret = ECMD_FAILED;
return ret;
}
/*
* All lvresize command defs have their own function,
* so the generic function name is unused.
*/
int lvresize(struct cmd_context *cmd, int argc, char **argv)
{
log_error(INTERNAL_ERROR "Missing function for command definition %d:%s.",
cmd->command->command_index, command_enum(cmd->command->command_enum));
return ECMD_FAILED;
}