mirror of
git://sourceware.org/git/lvm2.git
synced 2024-10-29 21:55:56 +03:00
fcbef05aae
Hmm rpmlint suggest fsf is using a different address these days, so lets keep it up-to-date
1563 lines
44 KiB
C
1563 lines
44 KiB
C
/*
|
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2014 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"
|
|
|
|
#include <fcntl.h>
|
|
|
|
struct lvcreate_cmdline_params {
|
|
percent_type_t percent;
|
|
uint64_t size;
|
|
uint64_t virtual_size; /* snapshot, thin */
|
|
char **pvs;
|
|
uint32_t pv_count;
|
|
};
|
|
|
|
struct processing_params {
|
|
struct lvcreate_params *lp;
|
|
struct lvcreate_cmdline_params *lcp;
|
|
};
|
|
|
|
static int _set_vg_name(struct lvcreate_params *lp, const char *vg_name)
|
|
{
|
|
/* Can't do anything */
|
|
if (!vg_name)
|
|
return 1;
|
|
|
|
/* If VG name already known, ensure this 2nd copy is identical */
|
|
if (lp->vg_name && strcmp(lp->vg_name, vg_name)) {
|
|
log_error("Inconsistent volume group names "
|
|
"given: \"%s\" and \"%s\"",
|
|
lp->vg_name, vg_name);
|
|
return 0;
|
|
}
|
|
lp->vg_name = vg_name;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvcreate_name_params(struct cmd_context *cmd,
|
|
int *pargc, char ***pargv,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
int argc = *pargc;
|
|
char **argv = *pargv;
|
|
const char *vg_name;
|
|
|
|
lp->lv_name = arg_str_value(cmd, name_ARG, NULL);
|
|
if (!validate_restricted_lvname_param(cmd, &lp->vg_name, &lp->lv_name))
|
|
return_0;
|
|
|
|
lp->pool_name = arg_str_value(cmd, thinpool_ARG, NULL)
|
|
? : arg_str_value(cmd, cachepool_ARG, NULL);
|
|
if (!validate_lvname_param(cmd, &lp->vg_name, &lp->pool_name))
|
|
return_0;
|
|
|
|
if (seg_is_cache(lp)) {
|
|
/*
|
|
* 2 ways of cache usage for lvcreate -H -l1 vg/lv
|
|
*
|
|
* vg/lv is existing cache pool:
|
|
* cached LV is created using this cache pool
|
|
* vg/lv is not cache pool so it is cache origin
|
|
* origin is cached with created cache pool
|
|
*
|
|
* We are looking for the vgname or cache pool or cache origin LV.
|
|
*
|
|
* lv name is stored in origin_name and pool_name and
|
|
* later with opened VG it's decided what should happen.
|
|
*/
|
|
if (!argc) {
|
|
if (!lp->pool_name) {
|
|
log_error("Please specify a logical volume to act as the cache pool or origin.");
|
|
return 0;
|
|
}
|
|
} else {
|
|
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (!strchr(vg_name, '/')) {
|
|
/* Lucky part - only vgname is here */
|
|
if (!_set_vg_name(lp, vg_name))
|
|
return_0;
|
|
} else {
|
|
/* Assume it's cache origin for now */
|
|
lp->origin_name = vg_name;
|
|
if (!validate_lvname_param(cmd, &lp->vg_name, &lp->origin_name))
|
|
return_0;
|
|
|
|
if (lp->pool_name) {
|
|
if (strcmp(lp->pool_name, lp->origin_name)) {
|
|
log_error("Unsupported syntax, cannot use cache origin %s and --cachepool %s.",
|
|
lp->origin_name, lp->pool_name);
|
|
return 0;
|
|
}
|
|
lp->origin_name = NULL;
|
|
} else {
|
|
/*
|
|
* Gambling here, could be cache pool or cache origin,
|
|
* detection is possible after openning vg,
|
|
* yet we need to parse pool args
|
|
*/
|
|
lp->pool_name = lp->origin_name;
|
|
lp->create_pool = 1;
|
|
}
|
|
}
|
|
(*pargv)++, (*pargc)--;
|
|
}
|
|
|
|
if (!lp->vg_name &&
|
|
!_set_vg_name(lp, extract_vgname(cmd, NULL)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name) {
|
|
log_error("The cache pool or cache origin name should "
|
|
"include the volume group.");
|
|
return 0;
|
|
}
|
|
|
|
if (!lp->pool_name) {
|
|
log_error("Creation of cached volume and cache pool "
|
|
"in one command is not yet supported.");
|
|
return 0;
|
|
}
|
|
} else if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) {
|
|
/* argv[0] might be [vg/]origin */
|
|
if (!argc) {
|
|
log_error("Please specify a logical volume to act as "
|
|
"the snapshot origin.");
|
|
return 0;
|
|
}
|
|
|
|
lp->origin_name = argv[0];
|
|
if (!validate_lvname_param(cmd, &lp->vg_name, &lp->origin_name))
|
|
return_0;
|
|
|
|
if (!lp->vg_name &&
|
|
!_set_vg_name(lp, extract_vgname(cmd, NULL)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name) {
|
|
log_error("The origin name should include the "
|
|
"volume group.");
|
|
return 0;
|
|
}
|
|
|
|
(*pargv)++, (*pargc)--;
|
|
} else if ((seg_is_thin(lp) || seg_is_pool(lp)) && argc) {
|
|
/* argv[0] might be [/dev.../]vg or [/dev../]vg/pool */
|
|
|
|
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (!strchr(vg_name, '/')) {
|
|
if (lp->snapshot && arg_is_set(cmd, virtualsize_ARG))
|
|
lp->snapshot = 0 ; /* Sparse volume via thin-pool */
|
|
if (!_set_vg_name(lp, vg_name))
|
|
return_0;
|
|
} else {
|
|
if (!validate_lvname_param(cmd, &lp->vg_name, &vg_name))
|
|
return_0;
|
|
|
|
if (lp->pool_name &&
|
|
(strcmp(vg_name, lp->pool_name) != 0)) {
|
|
log_error("Ambiguous %s name specified, %s and %s.",
|
|
lp->segtype->name, vg_name, lp->pool_name);
|
|
return 0;
|
|
}
|
|
lp->pool_name = vg_name;
|
|
|
|
if (!lp->vg_name &&
|
|
!_set_vg_name(lp, extract_vgname(cmd, NULL)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name) {
|
|
log_error("The %s name should include the "
|
|
"volume group.", lp->segtype->name);
|
|
return 0;
|
|
}
|
|
}
|
|
(*pargv)++, (*pargc)--;
|
|
} else {
|
|
/*
|
|
* If VG not on command line, try environment default.
|
|
*/
|
|
if (!argc) {
|
|
if (!lp->vg_name && !(lp->vg_name = extract_vgname(cmd, NULL))) {
|
|
log_error("Please provide a volume group name");
|
|
return 0;
|
|
}
|
|
} else {
|
|
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (strchr(vg_name, '/')) {
|
|
log_error("Volume group name expected "
|
|
"(no slash)");
|
|
return 0;
|
|
}
|
|
|
|
if (!_set_vg_name(lp, vg_name))
|
|
return_0;
|
|
|
|
(*pargv)++, (*pargc)--;
|
|
}
|
|
}
|
|
|
|
/* support --name & --type {thin|cache}-pool */
|
|
if (seg_is_pool(lp) && lp->lv_name) {
|
|
if (lp->pool_name && (strcmp(lp->lv_name, lp->pool_name) != 0)) {
|
|
log_error("Ambiguous %s name specified, %s and %s.",
|
|
lp->segtype->name, lp->lv_name, lp->pool_name);
|
|
return 0;
|
|
}
|
|
lp->pool_name = lp->lv_name;
|
|
lp->lv_name = NULL;
|
|
}
|
|
|
|
if (lp->pool_name && lp->lv_name && !strcmp(lp->pool_name, lp->lv_name)) {
|
|
log_error("Logical volume name %s and pool name must be different.",
|
|
lp->lv_name);
|
|
return 0;
|
|
}
|
|
|
|
if (!validate_name(lp->vg_name)) {
|
|
log_error("Volume group name %s has invalid characters",
|
|
lp->vg_name);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Update extents parameters based on other parameters which affect the size
|
|
* calculation.
|
|
* NOTE: We must do this here because of the dm_percent_t typedef and because we
|
|
* need the vg.
|
|
*/
|
|
static int _update_extents_params(struct volume_group *vg,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
uint32_t pv_extent_count;
|
|
struct logical_volume *origin_lv = NULL;
|
|
uint32_t size_rest;
|
|
uint32_t stripesize_extents;
|
|
uint32_t extents;
|
|
|
|
if (lcp->size &&
|
|
!(lp->extents = extents_from_size(vg->cmd, lcp->size,
|
|
vg->extent_size)))
|
|
return_0;
|
|
|
|
if (lcp->virtual_size &&
|
|
!(lp->virtual_extents = extents_from_size(vg->cmd, lcp->virtual_size,
|
|
vg->extent_size)))
|
|
return_0;
|
|
|
|
/*
|
|
* Create the pv list before we parse lcp->percent - might be
|
|
* PERCENT_PVSs
|
|
*/
|
|
if (lcp->pv_count) {
|
|
if (!(lp->pvh = create_pv_list(vg->cmd->mem, vg,
|
|
lcp->pv_count, lcp->pvs, 1)))
|
|
return_0;
|
|
} else
|
|
lp->pvh = &vg->pvs;
|
|
|
|
switch (lcp->percent) {
|
|
case PERCENT_VG:
|
|
extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
|
break;
|
|
case PERCENT_FREE:
|
|
extents = percent_of_extents(lp->extents, vg->free_count, 0);
|
|
break;
|
|
case PERCENT_PVS:
|
|
if (lcp->pv_count) {
|
|
pv_extent_count = pv_list_extents_free(lp->pvh);
|
|
extents = percent_of_extents(lp->extents, pv_extent_count, 0);
|
|
} else
|
|
extents = percent_of_extents(lp->extents, vg->extent_count, 0);
|
|
break;
|
|
case PERCENT_LV:
|
|
log_error("Please express size as %%FREE%s, %%PVS or %%VG.",
|
|
(lp->snapshot) ? ", %ORIGIN" : "");
|
|
return 0;
|
|
case PERCENT_ORIGIN:
|
|
if (lp->snapshot && lp->origin_name &&
|
|
!(origin_lv = find_lv(vg, lp->origin_name))) {
|
|
log_error("Couldn't find origin volume '%s'.",
|
|
lp->origin_name);
|
|
return 0;
|
|
}
|
|
if (!origin_lv) {
|
|
log_error(INTERNAL_ERROR "Couldn't find origin volume.");
|
|
return 0;
|
|
}
|
|
/* Add whole metadata size estimation */
|
|
extents = cow_max_extents(origin_lv, lp->chunk_size) - origin_lv->le_count +
|
|
percent_of_extents(lp->extents, origin_lv->le_count, 1);
|
|
break;
|
|
case PERCENT_NONE:
|
|
extents = lp->extents;
|
|
break;
|
|
default:
|
|
log_error(INTERNAL_ERROR "Unsupported percent type %u.", lcp->percent);
|
|
return 0;
|
|
}
|
|
|
|
if (lcp->percent) {
|
|
/* FIXME Don't do the adjustment for parallel allocation with PERCENT_ORIGIN! */
|
|
lp->approx_alloc = 1;
|
|
log_verbose("Converted %" PRIu32 "%%%s into %" PRIu32 " extents.", lp->extents, get_percent_string(lcp->percent), extents);
|
|
lp->extents = extents;
|
|
}
|
|
|
|
if (lp->snapshot && lp->origin_name && lp->extents) {
|
|
if (!lp->chunk_size) {
|
|
log_error(INTERNAL_ERROR "Missing snapshot chunk size.");
|
|
return 0;
|
|
}
|
|
|
|
if (!origin_lv && !(origin_lv = find_lv(vg, lp->origin_name))) {
|
|
log_error("Couldn't find origin volume '%s'.",
|
|
lp->origin_name);
|
|
return 0;
|
|
}
|
|
|
|
extents = cow_max_extents(origin_lv, lp->chunk_size);
|
|
|
|
if (extents < lp->extents) {
|
|
log_print_unless_silent("Reducing COW size %s down to maximum usable size %s.",
|
|
display_size(vg->cmd, (uint64_t) vg->extent_size * lp->extents),
|
|
display_size(vg->cmd, (uint64_t) vg->extent_size * extents));
|
|
lp->extents = extents;
|
|
}
|
|
}
|
|
|
|
if (!(stripesize_extents = lp->stripe_size / vg->extent_size))
|
|
stripesize_extents = 1;
|
|
|
|
if ((lcp->percent != PERCENT_NONE) && lp->stripes &&
|
|
(size_rest = lp->extents % (lp->stripes * stripesize_extents)) &&
|
|
(vg->free_count < lp->extents - size_rest + (lp->stripes * stripesize_extents))) {
|
|
log_print_unless_silent("Rounding size (%d extents) down to stripe boundary "
|
|
"size (%d extents)", lp->extents,
|
|
lp->extents - size_rest);
|
|
lp->extents = lp->extents - size_rest;
|
|
}
|
|
|
|
if (lp->create_pool) {
|
|
if (lp->pool_metadata_size &&
|
|
!(lp->pool_metadata_extents =
|
|
extents_from_size(vg->cmd, lp->pool_metadata_size, vg->extent_size)))
|
|
return_0;
|
|
|
|
if (!update_pool_params(lp->segtype, vg, lp->target_attr,
|
|
lp->passed_args, lp->extents,
|
|
&lp->pool_metadata_extents,
|
|
&lp->thin_chunk_size_calc_policy, &lp->chunk_size,
|
|
&lp->discards, &lp->zero))
|
|
return_0;
|
|
|
|
if (lcp->percent == PERCENT_FREE) {
|
|
if (lp->extents <= (2 * lp->pool_metadata_extents)) {
|
|
log_error("Not enough space for thin pool creation.");
|
|
return 0;
|
|
}
|
|
/* FIXME: persistent hidden space in VG wanted */
|
|
lp->extents -= (2 * lp->pool_metadata_extents);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Validate various common size arguments
|
|
*
|
|
* Note: at this place all volume types needs to be already
|
|
* identified, do not change them here.
|
|
*/
|
|
static int _read_size_params(struct cmd_context *cmd,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
if (arg_from_list_is_negative(cmd, "may not be negative",
|
|
chunksize_ARG, extents_ARG,
|
|
mirrors_ARG,
|
|
maxrecoveryrate_ARG,
|
|
minrecoveryrate_ARG,
|
|
regionsize_ARG,
|
|
size_ARG,
|
|
stripes_ARG, stripesize_ARG,
|
|
virtualsize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (arg_from_list_is_zero(cmd, "may not be zero",
|
|
chunksize_ARG, extents_ARG,
|
|
regionsize_ARG,
|
|
size_ARG,
|
|
stripes_ARG, stripesize_ARG,
|
|
virtualsize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
lcp->virtual_size = arg_uint64_value(cmd, virtualsize_ARG, UINT64_C(0));
|
|
|
|
if (arg_count(cmd, extents_ARG)) {
|
|
if (arg_count(cmd, size_ARG)) {
|
|
log_error("Please specify either size or extents (not both).");
|
|
return 0;
|
|
}
|
|
lp->extents = arg_uint_value(cmd, extents_ARG, 0);
|
|
lcp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
|
|
} else if (arg_count(cmd, size_ARG)) {
|
|
lcp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
|
|
lcp->percent = PERCENT_NONE;
|
|
} else if (!lp->snapshot && !seg_is_thin_volume(lp)) {
|
|
log_error("Please specify either size or extents.");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Read parameters related to mirrors
|
|
*/
|
|
static int _read_mirror_params(struct cmd_context *cmd,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
int corelog = arg_is_set(cmd, corelog_ARG);
|
|
|
|
lp->log_count = arg_int_value(cmd, mirrorlog_ARG, corelog ? 0 : DEFAULT_MIRRORLOG);
|
|
|
|
if (corelog && (lp->log_count != 0)) {
|
|
log_error("Please use only one of --corelog or --mirrorlog.");
|
|
return 0;
|
|
}
|
|
|
|
log_verbose("Setting logging type to %s", get_mirror_log_name(lp->log_count));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Read parameters related to raids
|
|
*/
|
|
static int _read_raid_params(struct cmd_context *cmd,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
if ((lp->stripes < 2) && segtype_is_raid10(lp->segtype)) {
|
|
if (arg_count(cmd, stripes_ARG)) {
|
|
/* User supplied the bad argument */
|
|
log_error("Segment type 'raid10' requires 2 or more stripes.");
|
|
return 0;
|
|
}
|
|
/* No stripe argument was given - default to 2 */
|
|
lp->stripes = 2;
|
|
lp->stripe_size = find_config_tree_int(cmd, metadata_stripesize_CFG, NULL) * 2;
|
|
}
|
|
|
|
/*
|
|
* RAID1 does not take a stripe arg
|
|
*/
|
|
if ((lp->stripes > 1) && seg_is_mirrored(lp) &&
|
|
!segtype_is_raid10(lp->segtype)) {
|
|
log_error("Stripe argument cannot be used with segment type, %s",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, mirrors_ARG) && segtype_is_raid(lp->segtype) &&
|
|
!segtype_is_raid1(lp->segtype) && !segtype_is_raid10(lp->segtype)) {
|
|
log_error("Mirror argument cannot be used with segment type, %s",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
/* Rates are recorded in kiB/sec/disk, not sectors/sec/disk */
|
|
lp->min_recovery_rate = arg_uint_value(cmd, minrecoveryrate_ARG, 0) / 2;
|
|
lp->max_recovery_rate = arg_uint_value(cmd, maxrecoveryrate_ARG, 0) / 2;
|
|
|
|
if (lp->min_recovery_rate > lp->max_recovery_rate) {
|
|
log_error("Minimum recovery rate cannot be higher than maximum.");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Read parameters related to mirrors and raids
|
|
*/
|
|
static int _read_mirror_and_raid_params(struct cmd_context *cmd,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
int pagesize = lvm_getpagesize();
|
|
unsigned max_images;
|
|
const char *segtype_name;
|
|
|
|
/* Common mirror and raid params */
|
|
if (arg_count(cmd, mirrors_ARG)) {
|
|
lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1;
|
|
if (segtype_is_raid1(lp->segtype)) {
|
|
segtype_name = SEG_TYPE_NAME_RAID1;
|
|
max_images = DEFAULT_RAID_MAX_IMAGES;
|
|
} else {
|
|
segtype_name = SEG_TYPE_NAME_MIRROR;
|
|
max_images = DEFAULT_MIRROR_MAX_IMAGES;
|
|
}
|
|
|
|
if (lp->mirrors > max_images) {
|
|
log_error("Only up to %u images in %s supported currently.",
|
|
max_images, segtype_name);
|
|
return 0;
|
|
}
|
|
|
|
if ((lp->mirrors > 2) && segtype_is_raid10(lp->segtype)) {
|
|
/*
|
|
* FIXME: When RAID10 is no longer limited to
|
|
* 2-way mirror, 'lv_mirror_count()'
|
|
* must also change for RAID10.
|
|
*/
|
|
log_error("RAID10 currently supports "
|
|
"only 2-way mirroring (i.e. '-m 1')");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->mirrors == 1) {
|
|
if (seg_is_mirrored(lp)) {
|
|
log_error("--mirrors must be at least 1 with segment type %s.", lp->segtype->name);
|
|
return 0;
|
|
}
|
|
log_print_unless_silent("Redundant mirrors argument: default is 0");
|
|
}
|
|
} else
|
|
/* Default to 2 mirrored areas if '--type mirror|raid1|raid10' */
|
|
lp->mirrors = seg_is_mirrored(lp) ? 2 : 1;
|
|
|
|
lp->nosync = arg_is_set(cmd, nosync_ARG);
|
|
|
|
if (!(lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0)) &&
|
|
((lp->region_size = get_default_region_size(cmd)) <= 0)) {
|
|
log_error("regionsize in configuration file is invalid.");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->region_size & (lp->region_size - 1)) {
|
|
log_error("Region size (%" PRIu32 ") must be a power of 2",
|
|
lp->region_size);
|
|
return 0;
|
|
}
|
|
|
|
if (lp->region_size % (pagesize >> SECTOR_SHIFT)) {
|
|
log_error("Region size (%" PRIu32 ") must be a multiple of "
|
|
"machine memory page size (%d)",
|
|
lp->region_size, pagesize >> SECTOR_SHIFT);
|
|
return 0;
|
|
}
|
|
|
|
if (seg_is_mirror(lp) && !_read_mirror_params(cmd, lp))
|
|
return_0;
|
|
|
|
if (seg_is_raid(lp) && !_read_raid_params(cmd, lp))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_cache_params(struct cmd_context *cmd,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
if (!seg_is_cache(lp) && !seg_is_cache_pool(lp))
|
|
return 1;
|
|
|
|
if (!get_cache_params(cmd,
|
|
&lp->cache_mode,
|
|
&lp->policy_name,
|
|
&lp->policy_settings))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_activation_params(struct cmd_context *cmd,
|
|
struct volume_group *vg,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
unsigned pagesize = lvm_getpagesize() >> SECTOR_SHIFT;
|
|
|
|
lp->activate = (activation_change_t)
|
|
arg_uint_value(cmd, activate_ARG, CHANGE_AY);
|
|
|
|
/* Error when full */
|
|
if (arg_is_set(cmd, errorwhenfull_ARG)) {
|
|
lp->error_when_full = arg_uint_value(cmd, errorwhenfull_ARG, 0);
|
|
} else
|
|
lp->error_when_full =
|
|
seg_can_error_when_full(lp) &&
|
|
find_config_tree_bool(cmd, activation_error_when_full_CFG, NULL);
|
|
|
|
/* Read ahead */
|
|
lp->read_ahead = arg_uint_value(cmd, readahead_ARG,
|
|
cmd->default_settings.read_ahead);
|
|
if (lp->read_ahead != DM_READ_AHEAD_AUTO &&
|
|
lp->read_ahead != DM_READ_AHEAD_NONE &&
|
|
lp->read_ahead % pagesize) {
|
|
if (lp->read_ahead < pagesize)
|
|
lp->read_ahead = pagesize;
|
|
else
|
|
lp->read_ahead = (lp->read_ahead / pagesize) * pagesize;
|
|
log_warn("WARNING: Overriding readahead to %u sectors, a multiple "
|
|
"of %uK page size.", lp->read_ahead, pagesize >> 1);
|
|
}
|
|
|
|
/* Persistent minor (and major), default 'n' */
|
|
if (!get_and_validate_major_minor(cmd, vg->fid->fmt, &lp->major, &lp->minor))
|
|
return_0;
|
|
|
|
if (arg_is_set(cmd, setactivationskip_ARG)) {
|
|
lp->activation_skip |= ACTIVATION_SKIP_SET;
|
|
if (arg_int_value(cmd, setactivationskip_ARG, 0))
|
|
lp->activation_skip |= ACTIVATION_SKIP_SET_ENABLED;
|
|
}
|
|
|
|
if (arg_is_set(cmd, ignoreactivationskip_ARG))
|
|
lp->activation_skip |= ACTIVATION_SKIP_IGNORE;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvcreate_params(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
int contiguous;
|
|
struct arg_value_group_list *current_group;
|
|
const char *segtype_str;
|
|
const char *tag;
|
|
int only_linear = 0;
|
|
int mirror_default_cfg;
|
|
|
|
dm_list_init(&lp->tags);
|
|
lp->target_attr = ~0;
|
|
lp->yes = arg_count(cmd, yes_ARG);
|
|
lp->force = (force_t) arg_count(cmd, force_ARG);
|
|
lp->permission = arg_uint_value(cmd, permission_ARG,
|
|
LVM_READ | LVM_WRITE);
|
|
|
|
/*
|
|
* --type is the top most rule
|
|
*
|
|
* Ordering of following type tests is IMPORTANT
|
|
*/
|
|
if ((segtype_str = arg_str_value(cmd, type_ARG, NULL))) {
|
|
lp->type = 1;
|
|
if (!strcmp(segtype_str, "linear")) {
|
|
segtype_str = "striped";
|
|
only_linear = 1; /* User requested linear only target */
|
|
}
|
|
/* More estimations from options after shortcuts */
|
|
} else if (arg_is_set(cmd, snapshot_ARG) &&
|
|
(arg_is_set(cmd, virtualoriginsize_ARG) ||
|
|
!arg_is_set(cmd, virtualsize_ARG)))
|
|
/* Snapshot has higher priority then thin */
|
|
segtype_str = SEG_TYPE_NAME_SNAPSHOT; /* --thinpool makes thin volume */
|
|
else if (arg_is_set(cmd, cache_ARG) || arg_is_set(cmd, cachepool_ARG))
|
|
segtype_str = SEG_TYPE_NAME_CACHE;
|
|
else if (arg_is_set(cmd, thin_ARG) || arg_is_set(cmd, thinpool_ARG))
|
|
segtype_str = SEG_TYPE_NAME_THIN;
|
|
else if (arg_is_set(cmd, virtualsize_ARG)) {
|
|
if (arg_is_set(cmd, virtualoriginsize_ARG))
|
|
segtype_str = SEG_TYPE_NAME_SNAPSHOT; /* --virtualoriginsize incompatible with pools */
|
|
else
|
|
segtype_str = find_config_tree_str(cmd, global_sparse_segtype_default_CFG, NULL);
|
|
} else if (arg_uint_value(cmd, mirrors_ARG, 0)) {
|
|
/* Remember, '-m 0' implies stripe */
|
|
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
|
|
segtype_str = SEG_TYPE_NAME_STRIPED;
|
|
|
|
if (!(lp->segtype = get_segtype_from_string(cmd, segtype_str)))
|
|
return_0;
|
|
|
|
if (seg_unknown(lp)) {
|
|
log_error("Unable to create LV with unknown segment type %s.", segtype_str);
|
|
return 0;
|
|
}
|
|
|
|
/* Starts basic option validation for every segment type */
|
|
|
|
/* FIXME Use these ARGS macros also in commands.h? */
|
|
/* ARGS are disjoint! sets of options */
|
|
#define LVCREATE_ARGS \
|
|
activate_ARG,\
|
|
addtag_ARG,\
|
|
alloc_ARG,\
|
|
autobackup_ARG,\
|
|
available_ARG,\
|
|
contiguous_ARG,\
|
|
ignoreactivationskip_ARG,\
|
|
ignoremonitoring_ARG,\
|
|
monitor_ARG,\
|
|
mirrors_ARG,\
|
|
name_ARG,\
|
|
noudevsync_ARG,\
|
|
permission_ARG,\
|
|
persistent_ARG,\
|
|
readahead_ARG,\
|
|
setactivationskip_ARG,\
|
|
test_ARG,\
|
|
type_ARG
|
|
|
|
#define CACHE_POOL_ARGS \
|
|
cachemode_ARG,\
|
|
cachepool_ARG,\
|
|
cachepolicy_ARG,\
|
|
cachesettings_ARG
|
|
|
|
#define MIRROR_ARGS \
|
|
corelog_ARG,\
|
|
mirrorlog_ARG
|
|
|
|
#define MIRROR_RAID_ARGS \
|
|
nosync_ARG,\
|
|
regionsize_ARG
|
|
|
|
#define PERSISTENT_ARGS \
|
|
major_ARG,\
|
|
minor_ARG
|
|
|
|
#define POOL_ARGS \
|
|
pooldatasize_ARG,\
|
|
poolmetadatasize_ARG,\
|
|
poolmetadataspare_ARG
|
|
|
|
#define RAID_ARGS \
|
|
maxrecoveryrate_ARG,\
|
|
minrecoveryrate_ARG,\
|
|
raidmaxrecoveryrate_ARG,\
|
|
raidminrecoveryrate_ARG
|
|
|
|
#define SIZE_ARGS \
|
|
extents_ARG,\
|
|
size_ARG,\
|
|
stripes_ARG,\
|
|
stripesize_ARG
|
|
|
|
#define THIN_POOL_ARGS \
|
|
discards_ARG,\
|
|
thinpool_ARG
|
|
|
|
/* Cache and cache-pool segment type */
|
|
if (seg_is_cache(lp)) {
|
|
/* Only supported with --type cache, -H, --cache */
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with cache",
|
|
CACHE_POOL_ARGS,
|
|
LVCREATE_ARGS,
|
|
PERSISTENT_ARGS,
|
|
POOL_ARGS,
|
|
SIZE_ARGS,
|
|
cache_ARG,
|
|
chunksize_ARG,
|
|
wipesignatures_ARG, zero_ARG,
|
|
-1))
|
|
return_0;
|
|
lp->create_pool = 1; /* Confirmed when opened VG */
|
|
} else if (seg_is_cache_pool(lp)) {
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with cache pools",
|
|
CACHE_POOL_ARGS,
|
|
LVCREATE_ARGS,
|
|
POOL_ARGS,
|
|
extents_ARG,
|
|
size_ARG,
|
|
cache_ARG,
|
|
chunksize_ARG,
|
|
-1))
|
|
return_0;
|
|
if (!(lp->permission & LVM_WRITE)) {
|
|
log_error("Cannot create read-only cache pool.");
|
|
return 0;
|
|
}
|
|
lp->create_pool = 1;
|
|
} else if (arg_from_list_is_set(cmd, "is supported only with cache",
|
|
cache_ARG, CACHE_POOL_ARGS,
|
|
-1))
|
|
return_0;
|
|
|
|
/* Snapshot segment type */
|
|
if (seg_is_snapshot(lp)) {
|
|
/* Only supported with --type snapshot, -s, --snapshot */
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with snapshots",
|
|
LVCREATE_ARGS,
|
|
PERSISTENT_ARGS,
|
|
SIZE_ARGS,
|
|
chunksize_ARG,
|
|
snapshot_ARG,
|
|
thinpool_ARG,
|
|
virtualoriginsize_ARG,
|
|
virtualsize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
/* FIXME Resolve this ambiguous case with --pooldatasize */
|
|
if (arg_is_set(cmd, thinpool_ARG)) {
|
|
if (lp->type) {
|
|
/* Unsupported with --type snapshot */
|
|
log_error("Snapshot segment type is incompatible with thin pools.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_from_list_is_set(cmd, "is unsupported with snapshots and --thinpool",
|
|
SIZE_ARGS,
|
|
chunksize_ARG,
|
|
virtualoriginsize_ARG,
|
|
virtualsize_ARG,
|
|
-1))
|
|
return_0;
|
|
}
|
|
|
|
/* Snapshot segment type needs size/extents */
|
|
if (lp->type && !arg_is_set(cmd, size_ARG) && !arg_is_set(cmd, extents_ARG)) {
|
|
log_error("Snapshot segment type requires size or extents.");
|
|
return 0;
|
|
}
|
|
|
|
lp->snapshot = 1; /* Free arg is snapshot origin */
|
|
} else if (arg_from_list_is_set(cmd, "is supported only with sparse snapshots",
|
|
virtualoriginsize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
/* Mirror segment type */
|
|
if (seg_is_mirror(lp)) {
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with mirrors",
|
|
LVCREATE_ARGS,
|
|
MIRROR_ARGS,
|
|
MIRROR_RAID_ARGS,
|
|
PERSISTENT_ARGS,
|
|
SIZE_ARGS,
|
|
wipesignatures_ARG, zero_ARG,
|
|
-1))
|
|
return_0;
|
|
} else if (arg_from_list_is_set(cmd, "is supported only with mirrors",
|
|
MIRROR_ARGS,
|
|
-1))
|
|
return_0;
|
|
|
|
/* Raid segment type */
|
|
if (seg_is_raid(lp)) {
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with raids",
|
|
LVCREATE_ARGS,
|
|
MIRROR_RAID_ARGS,
|
|
PERSISTENT_ARGS,
|
|
RAID_ARGS,
|
|
SIZE_ARGS,
|
|
wipesignatures_ARG, zero_ARG,
|
|
-1))
|
|
return_0;
|
|
} else if (arg_from_list_is_set(cmd, "is supported only with raids",
|
|
RAID_ARGS,
|
|
-1))
|
|
return_0;
|
|
|
|
/* Thin and thin-pool segment type */
|
|
if (seg_is_thin_volume(lp)) {
|
|
/* Only supported with --type thin, -T, --thin, -V */
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with thins",
|
|
LVCREATE_ARGS,
|
|
PERSISTENT_ARGS,
|
|
POOL_ARGS,
|
|
SIZE_ARGS,
|
|
THIN_POOL_ARGS,
|
|
chunksize_ARG,
|
|
errorwhenfull_ARG,
|
|
snapshot_ARG,
|
|
thin_ARG,
|
|
virtualsize_ARG,
|
|
wipesignatures_ARG, zero_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
/* If size/extents given with thin, then we are also creating a thin-pool */
|
|
if (arg_is_set(cmd, size_ARG) || arg_is_set(cmd, extents_ARG)) {
|
|
if (arg_is_set(cmd, pooldatasize_ARG)) {
|
|
log_error("Please specify either size or pooldatasize.");
|
|
return 0;
|
|
}
|
|
lp->create_pool = 1;
|
|
} else if (arg_from_list_is_set(cmd, "is supported only with thin pool creation",
|
|
POOL_ARGS,
|
|
SIZE_ARGS,
|
|
chunksize_ARG,
|
|
discards_ARG,
|
|
errorwhenfull_ARG,
|
|
zero_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (!arg_is_set(cmd, virtualsize_ARG)) {
|
|
/* Without virtual size could be creation of thin-pool or snapshot */
|
|
if (lp->create_pool) {
|
|
if (lp->type) {
|
|
log_error("Thin segment type requires --virtualsize.");
|
|
return 0;
|
|
}
|
|
|
|
log_debug_metadata("Switching from thin to thin pool segment type.");
|
|
if (!(lp->segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_THIN_POOL)))
|
|
return_0;
|
|
} else /* Parse free arg as snapshot origin */
|
|
lp->snapshot = 1;
|
|
} else if (arg_is_set(cmd, snapshot_ARG))
|
|
lp->snapshot = 1;
|
|
} else if (seg_is_thin_pool(lp)) {
|
|
if (arg_outside_list_is_set(cmd, "is unsupported with thin pools",
|
|
LVCREATE_ARGS,
|
|
POOL_ARGS,
|
|
SIZE_ARGS,
|
|
THIN_POOL_ARGS,
|
|
chunksize_ARG,
|
|
zero_ARG,
|
|
-1))
|
|
return_0;
|
|
if (!(lp->permission & LVM_WRITE)) {
|
|
log_error("Cannot create read-only thin pool.");
|
|
return 0;
|
|
}
|
|
lp->create_pool = 1;
|
|
} else if (!lp->snapshot &&
|
|
arg_from_list_is_set(cmd, "is supported only with thins",
|
|
thin_ARG, THIN_POOL_ARGS,
|
|
-1))
|
|
return_0;
|
|
|
|
/* Check options shared between more segment types */
|
|
if (!seg_is_mirror(lp) && !seg_is_raid(lp)) {
|
|
if (arg_from_list_is_set(cmd, "is supported only with mirrors or raids",
|
|
nosync_ARG,
|
|
regionsize_ARG,
|
|
-1))
|
|
return_0;
|
|
/* Let -m0 pass */
|
|
if (arg_int_value(cmd, mirrors_ARG, 0)) {
|
|
log_error("--mirrors is supported only with mirrors or raids");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!lp->create_pool && !lp->snapshot &&
|
|
arg_from_list_is_set(cmd, "is supported only with pools and snapshots",
|
|
chunksize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (!lp->snapshot && !seg_is_thin_volume(lp) &&
|
|
arg_from_list_is_set(cmd, "is supported only with sparse snapshots and thins",
|
|
virtualsize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (!seg_can_error_when_full(lp) && !lp->create_pool &&
|
|
arg_is_set(cmd, errorwhenfull_ARG)) {
|
|
log_error("Segment type %s does not support --errorwhenfull.", lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
/* Basic segment type validation finished here */
|
|
|
|
if (activation() && lp->segtype->ops->target_present) {
|
|
if (!lp->segtype->ops->target_present(cmd, NULL, &lp->target_attr)) {
|
|
log_error("%s: Required device-mapper target(s) not detected in your kernel.",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
if (segtype_is_raid10(lp->segtype) && !(lp->target_attr & RAID_FEATURE_RAID10)) {
|
|
log_error("RAID module does not support RAID10.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Should we zero/wipe signatures on the lv, default to 'y' */
|
|
lp->zero = arg_int_value(cmd, zero_ARG, 1);
|
|
|
|
if (arg_is_set(cmd, wipesignatures_ARG)) {
|
|
/* If -W/--wipesignatures is given on command line directly, respect it. */
|
|
lp->wipe_signatures = arg_int_value(cmd, wipesignatures_ARG, 1);
|
|
} else {
|
|
/*
|
|
* If -W/--wipesignatures is not given on command line,
|
|
* look at the allocation/wipe_signatures_when_zeroing_new_lvs
|
|
* to decide what should be done exactly.
|
|
*/
|
|
if (find_config_tree_bool(cmd, allocation_wipe_signatures_when_zeroing_new_lvs_CFG, NULL))
|
|
lp->wipe_signatures = lp->zero;
|
|
else
|
|
lp->wipe_signatures = 0;
|
|
}
|
|
|
|
if (!_lvcreate_name_params(cmd, &argc, &argv, lp) ||
|
|
!_read_size_params(cmd, lp, lcp) ||
|
|
!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) ||
|
|
(lp->create_pool &&
|
|
!get_pool_params(cmd, lp->segtype, &lp->passed_args,
|
|
&lp->pool_metadata_size, &lp->pool_metadata_spare,
|
|
&lp->chunk_size, &lp->discards, &lp->zero)) ||
|
|
!_read_cache_params(cmd, lp) ||
|
|
!_read_mirror_and_raid_params(cmd, lp))
|
|
return_0;
|
|
|
|
if (only_linear && lp->stripes > 1) {
|
|
log_error("Cannot use stripes with linear type.");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->snapshot && (lp->extents || lcp->size)) {
|
|
lp->chunk_size = arg_uint_value(cmd, chunksize_ARG, 8);
|
|
if (lp->chunk_size < 8 || lp->chunk_size > 1024 ||
|
|
(lp->chunk_size & (lp->chunk_size - 1))) {
|
|
log_error("Chunk size must be a power of 2 in the "
|
|
"range 4K to 512K.");
|
|
return 0;
|
|
}
|
|
log_verbose("Setting chunksize to %s.", display_size(cmd, lp->chunk_size));
|
|
}
|
|
|
|
/* Allocation parameters */
|
|
contiguous = arg_int_value(cmd, contiguous_ARG, 0);
|
|
lp->alloc = contiguous ? ALLOC_CONTIGUOUS : ALLOC_INHERIT;
|
|
lp->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, lp->alloc);
|
|
|
|
if (contiguous && (lp->alloc != ALLOC_CONTIGUOUS)) {
|
|
log_error("Conflicting contiguous and alloc arguments.");
|
|
return 0;
|
|
}
|
|
|
|
dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
|
|
if (!grouped_arg_is_set(current_group->arg_values, addtag_ARG))
|
|
continue;
|
|
|
|
if (!(tag = grouped_arg_str_value(current_group->arg_values, addtag_ARG, NULL))) {
|
|
log_error("Failed to get tag.");
|
|
return 0;
|
|
}
|
|
|
|
if (!str_list_add(cmd->mem, &lp->tags, tag)) {
|
|
log_error("Unable to allocate memory for tag %s.", tag);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
lcp->pv_count = argc;
|
|
lcp->pvs = argv;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* _determine_cache_argument
|
|
* @vg
|
|
* @lp
|
|
*
|
|
* 'lp->pool_name' is set with an LV that could be either the cache_pool name
|
|
* or the origin name of the cached LV which is being created.
|
|
* This function determines which it is and sets 'lp->origin_name' or
|
|
* 'lp->pool_name' appropriately.
|
|
*/
|
|
static int _determine_cache_argument(struct volume_group *vg,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
struct cmd_context *cmd = vg->cmd;
|
|
struct logical_volume *lv;
|
|
|
|
if (!lp->pool_name) {
|
|
lp->pool_name = lp->lv_name;
|
|
} else if ((lv = find_lv(vg, lp->pool_name)) && lv_is_cache_pool(lv)) {
|
|
if (!validate_lv_cache_create_pool(lv))
|
|
return_0;
|
|
/* Pool exists, create cache volume */
|
|
lp->create_pool = 0;
|
|
lp->origin_name = NULL;
|
|
/* If cache args not given, use those from cache pool */
|
|
if (!arg_is_set(cmd, chunksize_ARG))
|
|
lp->chunk_size = first_seg(lv)->chunk_size;
|
|
} else if (lv) {
|
|
/* Origin exists, create cache pool volume */
|
|
if (!validate_lv_cache_create_origin(lv))
|
|
return_0;
|
|
|
|
if (arg_is_set(cmd, permission_ARG) &&
|
|
((lp->permission & LVM_WRITE) != (lv->status & LVM_WRITE))) {
|
|
/* Reverting permissions on all error path is very complicated */
|
|
log_error("Change of volume permission is unsupported with cache conversion, use lvchange.");
|
|
return 0;
|
|
}
|
|
/* FIXME How to handle skip flag? */
|
|
if (arg_from_list_is_set(cmd, "is unsupported with cache conversion",
|
|
stripes_ARG,
|
|
stripesize_ARG,
|
|
setactivationskip_ARG,
|
|
ignoreactivationskip_ARG,
|
|
-1))
|
|
return_0; /* FIXME */
|
|
|
|
/* Put origin into resulting activation state first */
|
|
if (is_change_activating(lp->activate)) {
|
|
if ((lp->activate == CHANGE_AAY) &&
|
|
!lv_passes_auto_activation_filter(cmd, lv)) {
|
|
log_verbose("Skipping activation of cache origin %s.",
|
|
display_lvname(lv));
|
|
return 1;
|
|
} else if (!activate_lv_excl_local(cmd, lv)) {
|
|
log_error("Cannot activate cache origin %s.",
|
|
display_lvname(lv));
|
|
return 0;
|
|
}
|
|
} else if (!deactivate_lv(cmd, lv)) {
|
|
log_error("Cannot deactivate activate cache origin %s.",
|
|
display_lvname(lv));
|
|
return 0;
|
|
}
|
|
|
|
/* lp->origin_name is already equal to lp->pool_name */
|
|
lp->pool_name = lp->lv_name; /* --name is cache pool name */
|
|
/* No zeroing of an existing origin! */
|
|
lp->zero = lp->wipe_signatures = 0;
|
|
} else {
|
|
/* Cache pool and cache volume needs to be created */
|
|
lp->origin_name = NULL;
|
|
/* --pooldatasize is needed here */
|
|
log_error("Ambiguous syntax, please create --type cache-pool %s separately.",
|
|
lp->pool_name);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Normal snapshot or thinly-provisioned snapshot?
|
|
*/
|
|
static int _determine_snapshot_type(struct volume_group *vg,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
struct logical_volume *origin_lv, *pool_lv = NULL;
|
|
|
|
if (!(origin_lv = find_lv(vg, lp->origin_name))) {
|
|
log_error("Snapshot origin LV %s not found in Volume group %s.",
|
|
lp->origin_name, vg->name);
|
|
return 0;
|
|
}
|
|
if (lp->extents || lcp->size)
|
|
return 1; /* Size specified */
|
|
|
|
/* Check if we could make thin snapshot */
|
|
if (lp->pool_name) {
|
|
if (!(pool_lv = find_lv(vg, lp->pool_name))) {
|
|
log_error("Thin pool volume %s not found in Volume group %s.",
|
|
lp->pool_name, vg->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!lv_is_thin_pool(pool_lv)) {
|
|
log_error("Logical volume %s is not a thin pool volume.",
|
|
display_lvname(pool_lv));
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (!lv_is_thin_volume(origin_lv)) {
|
|
if (!seg_is_thin(lp))
|
|
log_error("Please specify either size or extents with snapshots.");
|
|
else
|
|
log_error("Logical volume %s is not a thin volume. "
|
|
"Thin snapshot supports only thin origins.",
|
|
display_lvname(origin_lv));
|
|
return 0;
|
|
}
|
|
/* Origin thin volume without size makes thin segment */
|
|
lp->pool_name = first_seg(origin_lv)->pool_lv->name;
|
|
}
|
|
|
|
log_debug_metadata("Switching from snapshot to thin segment type.");
|
|
if (!(lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_THIN)))
|
|
return_0;
|
|
lp->snapshot = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_raid_parameters(struct volume_group *vg,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
unsigned devs = lcp->pv_count ? : dm_list_size(&vg->pvs);
|
|
struct cmd_context *cmd = vg->cmd;
|
|
|
|
/*
|
|
* If number of devices was not supplied, we can infer from
|
|
* the PVs given.
|
|
*/
|
|
if (!seg_is_mirrored(lp)) {
|
|
if (!arg_count(cmd, stripes_ARG) &&
|
|
(devs > 2 * lp->segtype->parity_devs))
|
|
lp->stripes = devs - lp->segtype->parity_devs;
|
|
|
|
if (!lp->stripe_size)
|
|
lp->stripe_size = find_config_tree_int(cmd, metadata_stripesize_CFG, NULL) * 2;
|
|
|
|
if (lp->stripes <= lp->segtype->parity_devs) {
|
|
log_error("Number of stripes must be at least %d for %s",
|
|
lp->segtype->parity_devs + 1,
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
} else if (segtype_is_raid10(lp->segtype)) {
|
|
if (!arg_count(cmd, stripes_ARG))
|
|
lp->stripes = devs / lp->mirrors;
|
|
if (lp->stripes < 2) {
|
|
log_error("Unable to create RAID10 LV,"
|
|
" insufficient number of devices.");
|
|
return 0;
|
|
}
|
|
}
|
|
/* 'mirrors' defaults to 2 - not the number of PVs supplied */
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
if (seg_is_thin_volume(lp) && lp->snapshot) {
|
|
log_error("Please either create snapshot or thin volume.");
|
|
return 0;
|
|
}
|
|
|
|
if (!seg_is_thin_volume(lp) && !lp->snapshot) {
|
|
if (!lp->create_pool) {
|
|
/* Not even creating thin pool? */
|
|
log_error("Please specify device size(s).");
|
|
return 0;
|
|
}
|
|
} else if (!lp->create_pool) {
|
|
if (arg_from_list_is_set(vg->cmd, "is only available when creating thin pool",
|
|
alloc_ARG,
|
|
chunksize_ARG,
|
|
contiguous_ARG,
|
|
stripes_ARG,
|
|
zero_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (lcp->pv_count) {
|
|
log_error("Only specify Physical volumes when allocating thin pool.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _check_pool_parameters(struct cmd_context *cmd,
|
|
struct volume_group *vg,
|
|
struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
struct logical_volume *pool_lv;
|
|
|
|
if (!lp->create_pool &&
|
|
arg_from_list_is_set(cmd, "is only available with pools",
|
|
POOL_ARGS,
|
|
discards_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (!seg_is_cache(lp) &&
|
|
!seg_is_thin_volume(lp) &&
|
|
!seg_is_pool(lp)) {
|
|
if (lp->pool_name && !lp->snapshot) {
|
|
log_error("Segment type %s cannot use pool %s.",
|
|
lp->segtype->name, lp->pool_name);
|
|
return 0;
|
|
}
|
|
return 1; /* Pool unrelated types */
|
|
}
|
|
|
|
if (lp->create_pool) {
|
|
/* Given pool name needs to follow restrictions for created LV */
|
|
if (lp->pool_name) {
|
|
if (!apply_lvname_restrictions(lp->pool_name))
|
|
return_0;
|
|
/* We could check existance only when we have vg */
|
|
if (vg && find_lv(vg, lp->pool_name)) {
|
|
log_error("Logical volume %s already exists in Volume group %s.",
|
|
lp->pool_name, vg->name);
|
|
return 0;
|
|
}
|
|
}
|
|
if (seg_is_pool(lp)) {
|
|
if (lp->major != -1 || lp->minor != -1) {
|
|
log_error("Persistent major and minor numbers are unsupported with pools.");
|
|
return 0;
|
|
}
|
|
/* When creating just pool the pool_name needs to be in lv_name */
|
|
lp->lv_name = lp->pool_name;
|
|
} else if (vg) {
|
|
/* FIXME: what better to do with --readahead and pools? */
|
|
if (arg_is_set(cmd, readahead_ARG)) {
|
|
log_error("Ambigous --readahead parameter specified. Please use either with pool or volume.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/* Not creating new pool, but existing pool is needed */
|
|
if (!lp->pool_name) {
|
|
if (lp->snapshot)
|
|
/* Taking snapshot via 'lvcreate -T vg/origin' */
|
|
return 1;
|
|
log_error("Please specify name of existing pool.");
|
|
return 0;
|
|
}
|
|
|
|
if (vg) {
|
|
/* Validate pool has matching type */
|
|
if (!(pool_lv = find_lv(vg, lp->pool_name))) {
|
|
log_error("Pool %s not found in Volume group %s.",
|
|
lp->pool_name, vg->name);
|
|
return 0;
|
|
}
|
|
if (seg_is_cache(lp) && !lv_is_cache_pool(pool_lv)) {
|
|
log_error("Logical volume %s is not a cache pool.",
|
|
display_lvname(pool_lv));
|
|
return 0;
|
|
}
|
|
if (seg_is_thin_volume(lp) && !lv_is_thin_pool(pool_lv)) {
|
|
log_error("Logical volume %s is not a thin pool.",
|
|
display_lvname(pool_lv));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Check zero_ARG with default value set to value of wipesignatures_ARG
|
|
* with its default set to 'n'. So if user specifies on command line either
|
|
* -Zy or -Wy it will check for incompatible options will report error then.
|
|
*
|
|
* Catching cases like we cannot fulfill:
|
|
* lvcreate [-an][-pr][-aay][-ky] [-Zy][-Wy]
|
|
*/
|
|
static int _check_zero_parameters(struct cmd_context *cmd, struct lvcreate_params *lp)
|
|
{
|
|
char buf[NAME_LEN + 128];
|
|
|
|
/* -Z has different meaning for thins */
|
|
if (seg_is_thin(lp))
|
|
return 1;
|
|
|
|
/* If there is some problem, buffer will not be empty */
|
|
if (dm_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
|
|
lp->origin_name ? "origin " : "",
|
|
lp->origin_name ? : "",
|
|
lp->origin_name ? " " : "",
|
|
!(lp->permission & LVM_WRITE) ? "read-only " : "",
|
|
!is_change_activating(lp->activate) ? "inactive " : "",
|
|
(lp->activate == CHANGE_AAY) ? "auto activated " : "",
|
|
((lp->activation_skip & ACTIVATION_SKIP_SET_ENABLED) &&
|
|
!(lp->activation_skip & ACTIVATION_SKIP_IGNORE))
|
|
? "skipped from activation " : "") < 0) {
|
|
log_error(INTERNAL_ERROR "Buffer is too small for dm_snprintf().");
|
|
return 0;
|
|
}
|
|
|
|
if (buf[0] || (lp->segtype->flags & SEG_CANNOT_BE_ZEROED)) {
|
|
/* Found condition that prevents zeroing */
|
|
if (arg_int_value(cmd, zero_ARG, arg_int_value(cmd, wipesignatures_ARG, 0))) {
|
|
if (!(lp->segtype->flags & SEG_CANNOT_BE_ZEROED)) {
|
|
log_error("Cannot zero %slogical volume with option -Zy or -Wy.", buf);
|
|
return 0;
|
|
}
|
|
log_print_unless_silent("Ignoring option -Zy or -Wy for unzeroable %s volume.",
|
|
lp->segtype->name);
|
|
}
|
|
lp->zero = lp->wipe_signatures = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Ensure the set of thin parameters extracted from the command line is consistent.
|
|
*/
|
|
static int _validate_internal_thin_processing(const struct lvcreate_params *lp)
|
|
{
|
|
int r = 1;
|
|
|
|
/*
|
|
The final state should be one of:
|
|
thin create_pool snapshot origin pool
|
|
1 1 0 0 y/n - create new pool and a thin LV in it
|
|
1 0 0 0 y - create new thin LV in existing pool
|
|
0 1 0 0 y/n - create new pool only
|
|
1 0 1 1 y - create thin snapshot of existing thin LV
|
|
*/
|
|
|
|
if (!lp->create_pool && !lp->pool_name) {
|
|
log_error(INTERNAL_ERROR "--thinpool not identified.");
|
|
r = 0;
|
|
}
|
|
|
|
if ((!lp->origin_name && lp->snapshot) ||
|
|
(lp->origin_name && !lp->snapshot && !seg_is_thin_volume(lp))) {
|
|
log_error(INTERNAL_ERROR "Inconsistent snapshot and origin parameters identified.");
|
|
r = 0;
|
|
}
|
|
|
|
if (!lp->create_pool && !lp->snapshot && !seg_is_thin_volume(lp)) {
|
|
log_error(INTERNAL_ERROR "Failed to identify what type of thin target to use.");
|
|
r = 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static void _destroy_lvcreate_params(struct lvcreate_params *lp)
|
|
{
|
|
if (lp->policy_settings) {
|
|
dm_config_destroy(lp->policy_settings);
|
|
lp->policy_settings = NULL;
|
|
}
|
|
}
|
|
|
|
static int _lvcreate_single(struct cmd_context *cmd, const char *vg_name,
|
|
struct volume_group *vg, struct processing_handle *handle)
|
|
{
|
|
struct processing_params *pp = (struct processing_params *) handle->custom_handle;
|
|
struct lvcreate_params *lp = pp->lp;
|
|
struct lvcreate_cmdline_params *lcp = pp->lcp;
|
|
int ret = ECMD_FAILED;
|
|
|
|
if (!_read_activation_params(cmd, vg, lp))
|
|
goto_out;
|
|
|
|
/* Resolve segment types with opened VG */
|
|
if (lp->snapshot && lp->origin_name && !_determine_snapshot_type(vg, lp, lcp))
|
|
goto_out;
|
|
|
|
if (seg_is_cache(lp) && !_determine_cache_argument(vg, lp))
|
|
goto_out;
|
|
|
|
/* All types resolved at this point, now only validation steps */
|
|
if (seg_is_raid(lp) && !_check_raid_parameters(vg, lp, lcp))
|
|
goto_out;
|
|
|
|
if (seg_is_thin(lp) && !_check_thin_parameters(vg, lp, lcp))
|
|
goto_out;
|
|
|
|
if (!_check_pool_parameters(cmd, vg, lp, lcp))
|
|
goto_out;
|
|
|
|
/* All types are checked */
|
|
if (!_check_zero_parameters(cmd, lp))
|
|
return_0;
|
|
|
|
if (!_update_extents_params(vg, lp, lcp))
|
|
goto_out;
|
|
|
|
if (seg_is_thin(lp) && !_validate_internal_thin_processing(lp))
|
|
goto_out;
|
|
|
|
if (lp->create_pool) {
|
|
if (!handle_pool_metadata_spare(vg, lp->pool_metadata_extents,
|
|
lp->pvh, lp->pool_metadata_spare))
|
|
goto_out;
|
|
|
|
log_verbose("Making pool %s in VG %s using segtype %s",
|
|
lp->pool_name ? : "with generated name", lp->vg_name, lp->segtype->name);
|
|
}
|
|
|
|
if (vg->lock_type && !strcmp(vg->lock_type, "sanlock")) {
|
|
if (!handle_sanlock_lv(cmd, vg)) {
|
|
log_error("No space for sanlock lock, extend the internal lvmlock LV.");
|
|
goto_out;
|
|
}
|
|
}
|
|
|
|
if (seg_is_thin_volume(lp))
|
|
log_verbose("Making thin LV %s in pool %s in VG %s%s%s using segtype %s",
|
|
lp->lv_name ? : "with generated name",
|
|
lp->pool_name ? : "with generated name", lp->vg_name,
|
|
lp->snapshot ? " as snapshot of " : "",
|
|
lp->snapshot ? lp->origin_name : "", lp->segtype->name);
|
|
|
|
if (is_lockd_type(vg->lock_type))
|
|
lp->needs_lockd_init = 1;
|
|
|
|
if (!lv_create_single(vg, lp))
|
|
goto_out;
|
|
|
|
ret = ECMD_PROCESSED;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int lvcreate(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
struct processing_handle *handle = NULL;
|
|
struct processing_params pp;
|
|
struct lvcreate_params lp = {
|
|
.major = -1,
|
|
.minor = -1,
|
|
};
|
|
struct lvcreate_cmdline_params lcp = { 0 };
|
|
int ret;
|
|
|
|
if (!_lvcreate_params(cmd, argc, argv, &lp, &lcp)) {
|
|
stack;
|
|
return EINVALID_CMD_LINE;
|
|
}
|
|
|
|
if (!_check_pool_parameters(cmd, NULL, &lp, &lcp)) {
|
|
stack;
|
|
return EINVALID_CMD_LINE;
|
|
}
|
|
|
|
pp.lp = &lp;
|
|
pp.lcp = &lcp;
|
|
|
|
if (!(handle = init_processing_handle(cmd))) {
|
|
log_error("Failed to initialize processing handle.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
handle->custom_handle = &pp;
|
|
|
|
ret = process_each_vg(cmd, 0, NULL, lp.vg_name, READ_FOR_UPDATE, handle,
|
|
&_lvcreate_single);
|
|
|
|
_destroy_lvcreate_params(&lp);
|
|
destroy_processing_handle(cmd, handle);
|
|
return ret;
|
|
}
|