mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
1338 lines
36 KiB
C
1338 lines
36 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "tools.h"
|
|
|
|
#include <fcntl.h>
|
|
|
|
struct lvcreate_cmdline_params {
|
|
percent_type_t percent;
|
|
uint64_t size;
|
|
char **pvs;
|
|
uint32_t pv_count;
|
|
};
|
|
|
|
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 lvcreate_params *lp,
|
|
struct cmd_context *cmd,
|
|
int *pargc, char ***pargv)
|
|
{
|
|
int argc = *pargc;
|
|
char **argv = *pargv, *ptr;
|
|
const char *vg_name;
|
|
|
|
lp->pool = arg_str_value(cmd, thinpool_ARG, NULL);
|
|
|
|
/* If --thinpool contains VG name, extract it. */
|
|
if (lp->pool && strchr(lp->pool, '/')) {
|
|
if (!(lp->vg_name = extract_vgname(cmd, lp->pool)))
|
|
return 0;
|
|
/* Strip VG from pool */
|
|
if ((ptr = strrchr(lp->pool, (int) '/')))
|
|
lp->pool = ptr + 1;
|
|
}
|
|
|
|
lp->lv_name = arg_str_value(cmd, name_ARG, NULL);
|
|
|
|
/* If --name contains VG name, extract it. */
|
|
if (lp->lv_name && strchr(lp->lv_name, '/')) {
|
|
if (!_set_vg_name(lp, extract_vgname(cmd, lp->lv_name)))
|
|
return_0;
|
|
|
|
/* Strip VG from lv_name */
|
|
if ((ptr = strrchr(lp->lv_name, (int) '/')))
|
|
lp->lv_name = ptr + 1;
|
|
}
|
|
|
|
if (seg_is_cache(lp)) {
|
|
/*
|
|
* We are looking for the origin or cache_pool LV.
|
|
* Could be in the form 'lv' or 'vg/lv'
|
|
*
|
|
* We store the lv name in 'lp->origin' for now, but
|
|
* it must be accessed later (when we can look-up the
|
|
* LV in the VG) whether it is truly the origin that
|
|
* was specified, or whether it is the cache_pool.
|
|
*/
|
|
if (!argc) {
|
|
log_error("Please specify a logical volume to act as "
|
|
"the origin or cache_pool.");
|
|
return 0;
|
|
}
|
|
|
|
lp->origin = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (strrchr(lp->origin, '/')) {
|
|
if (!_set_vg_name(lp, extract_vgname(cmd, lp->origin)))
|
|
return_0;
|
|
|
|
/* Strip the volume group from the origin */
|
|
if ((ptr = strrchr(lp->origin, (int) '/')))
|
|
lp->origin = ptr + 1;
|
|
}
|
|
|
|
if (!lp->vg_name &&
|
|
!_set_vg_name(lp, extract_vgname(cmd, NULL)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name) {
|
|
log_error("The origin or cache_pool name should include"
|
|
" the volume group.");
|
|
return 0;
|
|
}
|
|
|
|
lp->cache = 1;
|
|
(*pargv)++, (*pargc)--;
|
|
} else if (lp->snapshot && !arg_count(cmd, virtualsize_ARG)) {
|
|
/* argv[0] might be origin or vg/origin */
|
|
if (!argc) {
|
|
log_error("Please specify a logical volume to act as "
|
|
"the snapshot origin.");
|
|
return 0;
|
|
}
|
|
|
|
lp->origin = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (strrchr(lp->origin, '/')) {
|
|
if (!_set_vg_name(lp, extract_vgname(cmd, lp->origin)))
|
|
return_0;
|
|
|
|
/* Strip the volume group from the origin */
|
|
if ((ptr = strrchr(lp->origin, (int) '/')))
|
|
lp->origin = ptr + 1;
|
|
}
|
|
|
|
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) && !lp->pool && argc) {
|
|
/* argv[0] might be vg or vg/Pool */
|
|
|
|
vg_name = skip_dev_dir(cmd, argv[0], NULL);
|
|
if (!strrchr(vg_name, '/')) {
|
|
if (!_set_vg_name(lp, vg_name))
|
|
return_0;
|
|
} else {
|
|
lp->pool = vg_name;
|
|
if (!_set_vg_name(lp, extract_vgname(cmd, lp->pool)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name &&
|
|
!_set_vg_name(lp, extract_vgname(cmd, NULL)))
|
|
return_0;
|
|
|
|
if (!lp->vg_name) {
|
|
log_error("The pool name should include the "
|
|
"volume group.");
|
|
return 0;
|
|
}
|
|
|
|
/* Strip the volume group */
|
|
if ((ptr = strrchr(lp->pool, (int) '/')))
|
|
lp->pool = ptr + 1;
|
|
}
|
|
|
|
(*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 (strrchr(vg_name, '/')) {
|
|
log_error("Volume group name expected "
|
|
"(no slash)");
|
|
return 0;
|
|
}
|
|
|
|
if (!_set_vg_name(lp, vg_name))
|
|
return_0;
|
|
|
|
(*pargv)++, (*pargc)--;
|
|
}
|
|
}
|
|
|
|
if (!validate_name(lp->vg_name)) {
|
|
log_error("Volume group name %s has invalid characters",
|
|
lp->vg_name);
|
|
return 0;
|
|
}
|
|
|
|
if (lp->lv_name) {
|
|
if (!apply_lvname_restrictions(lp->lv_name))
|
|
return_0;
|
|
|
|
if (!validate_name(lp->lv_name)) {
|
|
log_error("Logical volume name \"%s\" is invalid",
|
|
lp->lv_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (lp->pool) {
|
|
if (!apply_lvname_restrictions(lp->pool))
|
|
return_0;
|
|
|
|
if (!validate_name(lp->pool)) {
|
|
log_error("Logical volume name \"%s\" is invalid",
|
|
lp->pool);
|
|
return 0;
|
|
}
|
|
|
|
if (lp->lv_name && !strcmp(lp->lv_name, lp->pool)) {
|
|
log_error("Logical volume name %s and pool name %s must be different.",
|
|
lp->lv_name, lp->pool);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Normal snapshot or thinly-provisioned snapshot?
|
|
*/
|
|
static int _determine_snapshot_type(struct volume_group *vg,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
struct lv_list *lvl;
|
|
|
|
if (!(lvl = find_lv_in_vg(vg, lp->origin))) {
|
|
log_error("Snapshot origin LV %s not found in Volume group %s.",
|
|
lp->origin, vg->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!arg_count(vg->cmd, extents_ARG) && !arg_count(vg->cmd, size_ARG)) {
|
|
if (seg_is_thin(lp)) {
|
|
if (!(lp->segtype = get_segtype_from_string(vg->cmd, "thin")))
|
|
return_0;
|
|
return 1;
|
|
}
|
|
|
|
if (!lv_is_thin_volume(lvl->lv)) {
|
|
log_error("Please specify either size or extents with snapshots.");
|
|
return 0;
|
|
}
|
|
|
|
if (!(lp->segtype = get_segtype_from_string(vg->cmd, "thin")))
|
|
return_0;
|
|
|
|
lp->pool = first_seg(lvl->lv)->pool_lv->name;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvcreate_update_pool_params(struct volume_group *vg,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
if (seg_is_cache_pool(lp))
|
|
return update_cache_pool_params(vg, lp->target_attr,
|
|
lp->passed_args,
|
|
lp->extents, vg->extent_size,
|
|
&lp->thin_chunk_size_calc_policy,
|
|
&lp->chunk_size, &lp->discards,
|
|
&lp->poolmetadatasize,
|
|
&lp->zero);
|
|
|
|
return update_thin_pool_params(vg, lp->target_attr, lp->passed_args,
|
|
lp->extents, vg->extent_size,
|
|
&lp->thin_chunk_size_calc_policy,
|
|
&lp->chunk_size, &lp->discards,
|
|
&lp->poolmetadatasize, &lp->zero);
|
|
}
|
|
|
|
/*
|
|
* _determine_cache_argument
|
|
* @vg
|
|
* @lp
|
|
*
|
|
* 'lp->origin' is set with an LV that could be either the origin
|
|
* or the cache_pool of the cached LV which is being created. This
|
|
* function determines which it is and sets 'lp->origin' or
|
|
* 'lp->pool' appropriately.
|
|
*/
|
|
static int _determine_cache_argument(struct volume_group *vg,
|
|
struct lvcreate_params *lp)
|
|
{
|
|
struct lv_list *lvl;
|
|
|
|
if (!seg_is_cache(lp)) {
|
|
log_error(INTERNAL_ERROR
|
|
"Unable to determine cache argument on %s segtype",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
if (!lp->origin) {
|
|
log_error(INTERNAL_ERROR "Origin LV is not defined.");
|
|
return 0;
|
|
}
|
|
if (!(lvl = find_lv_in_vg(vg, lp->origin))) {
|
|
log_error("LV %s not found in Volume group %s.",
|
|
lp->origin, vg->name);
|
|
return 0;
|
|
}
|
|
|
|
if (lv_is_cache_pool(lvl->lv)) {
|
|
lp->pool = lp->origin;
|
|
lp->origin = NULL;
|
|
} else {
|
|
lp->pool = NULL;
|
|
lp->create_pool = 1;
|
|
lp->poolmetadataspare = arg_int_value(vg->cmd,
|
|
poolmetadataspare_ARG,
|
|
DEFAULT_POOL_METADATA_SPARE);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Update extents parameters based on other parameters which affect the size
|
|
* calculation.
|
|
* NOTE: We must do this here because of the 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 = 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 (lp->voriginsize &&
|
|
!(lp->voriginextents = extents_from_size(vg->cmd, lp->voriginsize,
|
|
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 %%VG, %%PVS, or "
|
|
"%%FREE.");
|
|
return 0;
|
|
case PERCENT_ORIGIN:
|
|
if (lp->snapshot && lp->origin &&
|
|
!(origin = find_lv(vg, lp->origin))) {
|
|
log_error("Couldn't find origin volume '%s'.",
|
|
lp->origin);
|
|
return 0;
|
|
}
|
|
if (!origin) {
|
|
log_error(INTERNAL_ERROR "Couldn't find origin volume.");
|
|
return 0;
|
|
}
|
|
extents = percent_of_extents(lp->extents, origin->le_count, 0);
|
|
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 && lp->extents) {
|
|
if (!lp->chunk_size) {
|
|
log_error(INTERNAL_ERROR "Missing snapshot chunk size.");
|
|
return 0;
|
|
}
|
|
|
|
if (!origin && !(origin = find_lv(vg, lp->origin))) {
|
|
log_error("Couldn't find origin volume '%s'.",
|
|
lp->origin);
|
|
return 0;
|
|
}
|
|
|
|
extents = cow_max_extents(origin, 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 (!_lvcreate_update_pool_params(vg, lp))
|
|
return_0;
|
|
|
|
if (!(lp->poolmetadataextents =
|
|
extents_from_size(vg->cmd, lp->poolmetadatasize, vg->extent_size)))
|
|
return_0;
|
|
if (lcp->percent == PERCENT_FREE) {
|
|
if (lp->extents <= (2 * lp->poolmetadataextents)) {
|
|
log_error("Not enough space for thin pool creation.");
|
|
return 0;
|
|
}
|
|
/* FIXME: persistent hidden space in VG wanted */
|
|
lp->extents -= (2 * lp->poolmetadataextents);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_size_params(struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp,
|
|
struct cmd_context *cmd)
|
|
{
|
|
if (arg_count(cmd, extents_ARG) && arg_count(cmd, size_ARG)) {
|
|
log_error("Please specify either size or extents (not both)");
|
|
return 0;
|
|
}
|
|
|
|
if (!lp->thin && !lp->snapshot && !arg_count(cmd, extents_ARG) && !arg_count(cmd, size_ARG)) {
|
|
log_error("Please specify either size or extents");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, extents_ARG)) {
|
|
if (arg_sign_value(cmd, extents_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative number of extents is invalid");
|
|
return 0;
|
|
}
|
|
lp->extents = arg_uint_value(cmd, extents_ARG, 0);
|
|
lcp->percent = arg_percent_value(cmd, extents_ARG, PERCENT_NONE);
|
|
}
|
|
|
|
/* Size returned in kilobyte units; held in sectors */
|
|
if (arg_count(cmd, size_ARG)) {
|
|
if (arg_sign_value(cmd, size_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative size is invalid");
|
|
return 0;
|
|
}
|
|
lcp->size = arg_uint64_value(cmd, size_ARG, UINT64_C(0));
|
|
lcp->percent = PERCENT_NONE;
|
|
}
|
|
|
|
/* If size/extents given with thin, then we are creating a thin pool */
|
|
if (seg_is_thin(lp) && (arg_count(cmd, size_ARG) || arg_count(cmd, extents_ARG)))
|
|
lp->create_pool = 1;
|
|
|
|
if (!lp->create_pool && arg_count(cmd, poolmetadatasize_ARG)) {
|
|
log_error("--poolmetadatasize may only be specified when allocating the thin pool.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, virtualsize_ARG)) {
|
|
if (seg_is_thin_pool(lp)) {
|
|
log_error("Virtual size in incompatible with thin_pool segment type.");
|
|
return 0;
|
|
}
|
|
if (arg_sign_value(cmd, virtualsize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative virtual origin size is invalid");
|
|
return 0;
|
|
}
|
|
/* Size returned in kilobyte units; held in sectors */
|
|
lp->voriginsize = arg_uint64_value(cmd, virtualsize_ARG,
|
|
UINT64_C(0));
|
|
if (!lp->voriginsize) {
|
|
log_error("Virtual origin size may not be zero");
|
|
return 0;
|
|
}
|
|
} else {
|
|
/* No virtual size given and no snapshot, so no thin LV to create. */
|
|
if (seg_is_thin_volume(lp) && !lp->snapshot &&
|
|
!(lp->segtype = get_segtype_from_string(cmd, "thin-pool")))
|
|
return_0;
|
|
|
|
lp->thin = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Generic mirror parameter checks.
|
|
* FIXME: Should eventually be moved into lvm library.
|
|
*/
|
|
static int _validate_mirror_params(const struct cmd_context *cmd __attribute__((unused)),
|
|
const struct lvcreate_params *lp)
|
|
{
|
|
int pagesize = lvm_getpagesize();
|
|
|
|
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 (!lp->region_size) {
|
|
log_error("Non-zero region size must be supplied.");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_mirror_params(struct lvcreate_params *lp,
|
|
struct cmd_context *cmd)
|
|
{
|
|
int region_size;
|
|
const char *mirrorlog;
|
|
int corelog = arg_count(cmd, corelog_ARG);
|
|
|
|
mirrorlog = arg_str_value(cmd, mirrorlog_ARG,
|
|
corelog ? "core" : DEFAULT_MIRRORLOG);
|
|
|
|
if (strcmp("core", mirrorlog) && corelog) {
|
|
log_error("Please use only one of --mirrorlog or --corelog");
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp("mirrored", mirrorlog)) {
|
|
lp->log_count = 2;
|
|
} else if (!strcmp("disk", mirrorlog)) {
|
|
lp->log_count = 1;
|
|
} else if (!strcmp("core", mirrorlog))
|
|
lp->log_count = 0;
|
|
else {
|
|
log_error("Unknown mirrorlog type: %s", mirrorlog);
|
|
return 0;
|
|
}
|
|
|
|
log_verbose("Setting logging type to %s", mirrorlog);
|
|
|
|
lp->nosync = arg_is_set(cmd, nosync_ARG);
|
|
|
|
if (arg_count(cmd, regionsize_ARG)) {
|
|
if (arg_sign_value(cmd, regionsize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative regionsize is invalid");
|
|
return 0;
|
|
}
|
|
lp->region_size = arg_uint_value(cmd, regionsize_ARG, 0);
|
|
} else {
|
|
region_size = get_default_region_size(cmd);
|
|
if (region_size < 0) {
|
|
log_error("Negative regionsize in configuration file "
|
|
"is invalid");
|
|
return 0;
|
|
}
|
|
lp->region_size = region_size;
|
|
}
|
|
|
|
if (!_validate_mirror_params(cmd, lp))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_raid_params(struct lvcreate_params *lp,
|
|
struct cmd_context *cmd)
|
|
{
|
|
if (!segtype_is_raid(lp->segtype))
|
|
return 1;
|
|
|
|
if (arg_count(cmd, corelog_ARG) ||
|
|
arg_count(cmd, mirrorlog_ARG)) {
|
|
log_error("Log options not applicable to %s segtype",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp(lp->segtype->name, "raid10") && (lp->stripes < 2)) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* RAID types without a mirror component do not take '-m' arg
|
|
*/
|
|
if (!segtype_is_mirrored(lp->segtype) &&
|
|
arg_count(cmd, mirrors_ARG)) {
|
|
log_error("Mirror argument cannot be used with segment type, %s",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* RAID1 does not take a stripe arg
|
|
*/
|
|
if ((lp->stripes > 1) &&
|
|
segtype_is_mirrored(lp->segtype) &&
|
|
strcmp(lp->segtype->name, "raid10")) {
|
|
log_error("Stripe argument cannot be used with segment type, %s",
|
|
lp->segtype->name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* _read_mirror_params is called before _read_raid_params
|
|
* and already sets:
|
|
* lp->nosync
|
|
* lp->region_size
|
|
*
|
|
* But let's ensure that programmers don't reorder
|
|
* that by checking and warning if they aren't set.
|
|
*/
|
|
if (!lp->region_size) {
|
|
log_error(INTERNAL_ERROR "region_size not set.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, minrecoveryrate_ARG))
|
|
lp->min_recovery_rate = arg_uint_value(cmd,
|
|
minrecoveryrate_ARG, 0);
|
|
if (arg_count(cmd, maxrecoveryrate_ARG))
|
|
lp->max_recovery_rate = arg_uint_value(cmd,
|
|
maxrecoveryrate_ARG, 0);
|
|
|
|
/* Rates are recorded in kiB/sec/disk, not sectors/sec/disk */
|
|
lp->min_recovery_rate /= 2;
|
|
lp->max_recovery_rate /= 2;
|
|
|
|
if (lp->max_recovery_rate &&
|
|
(lp->max_recovery_rate < lp->min_recovery_rate)) {
|
|
log_error("Minimum recovery rate cannot be higher than maximum.");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int _read_cache_pool_params(struct lvcreate_params *lp,
|
|
struct cmd_context *cmd)
|
|
{
|
|
const char *str_arg;
|
|
|
|
if (!segtype_is_cache_pool(lp->segtype))
|
|
return 1;
|
|
|
|
if (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,
|
|
DEFAULT_CACHE_POOL_CHUNK_SIZE * 2);
|
|
|
|
str_arg = arg_str_value(cmd, cachemode_ARG, NULL);
|
|
if (str_arg) {
|
|
if (!strcmp(str_arg, "writeback"))
|
|
lp->feature_flags |= DM_CACHE_FEATURE_WRITEBACK;
|
|
else if (!strcmp(str_arg, "writethrough"))
|
|
lp->feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
|
|
else {
|
|
log_error("Unknown cachemode argument");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _read_activation_params(struct lvcreate_params *lp,
|
|
struct cmd_context *cmd,
|
|
struct volume_group *vg)
|
|
{
|
|
unsigned pagesize;
|
|
|
|
lp->activate = (activation_change_t)
|
|
arg_uint_value(cmd, activate_ARG, CHANGE_AY);
|
|
|
|
if (!is_change_activating(lp->activate)) {
|
|
if (lp->zero && !seg_is_thin(lp)) {
|
|
log_error("--activate n requires --zero n");
|
|
return 0;
|
|
}
|
|
} else if (lp->activate == CHANGE_AAY) {
|
|
if (arg_count(cmd, zero_ARG) || arg_count(cmd, wipesignatures_ARG)) {
|
|
log_error("-Z and -W is incompatible with --activate a");
|
|
return 0;
|
|
}
|
|
lp->zero = 0;
|
|
}
|
|
|
|
/*
|
|
* Read ahead.
|
|
*/
|
|
lp->read_ahead = arg_uint_value(cmd, readahead_ARG,
|
|
cmd->default_settings.read_ahead);
|
|
pagesize = lvm_getpagesize() >> SECTOR_SHIFT;
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Permissions.
|
|
*/
|
|
lp->permission = arg_uint_value(cmd, permission_ARG,
|
|
LVM_READ | LVM_WRITE);
|
|
|
|
if (lp->snapshot) {
|
|
/* Snapshot has to zero COW header */
|
|
lp->zero = 1;
|
|
lp->wipe_signatures = 0;
|
|
} else if (!(lp->permission & LVM_WRITE)) {
|
|
/* Must not zero/wipe read only volume */
|
|
lp->zero = 0;
|
|
lp->wipe_signatures = 0;
|
|
}
|
|
|
|
if (arg_count(cmd, major_ARG) > 1) {
|
|
log_error("Option -j/--major may not be repeated.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, minor_ARG) > 1) {
|
|
log_error("Option --minor may not be repeated.");
|
|
return 0;
|
|
}
|
|
|
|
lp->minor = arg_int_value(cmd, minor_ARG, -1);
|
|
lp->major = arg_int_value(cmd, major_ARG, -1);
|
|
|
|
/* Persistent minor */
|
|
if (arg_count(cmd, persistent_ARG)) {
|
|
if (lp->create_pool && !lp->thin) {
|
|
log_error("--persistent is not permitted when creating a thin pool device.");
|
|
return 0;
|
|
}
|
|
if (!strcmp(arg_str_value(cmd, persistent_ARG, "n"), "y")) {
|
|
if (lp->minor == -1) {
|
|
log_error("Please specify minor number with "
|
|
"--minor when using -My");
|
|
return 0;
|
|
}
|
|
if (!strncmp(cmd->kernel_vsn, "2.4.", 4)) {
|
|
if (lp->major == -1) {
|
|
log_error("Please specify major number with "
|
|
"--major when using -My");
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (lp->major >= 0)
|
|
log_warn("Ignoring supplied major number - kernel assigns "
|
|
"major numbers dynamically. Using major number %d instead.",
|
|
cmd->dev_types->device_mapper_major);
|
|
lp->major = cmd->dev_types->device_mapper_major;
|
|
}
|
|
if (!major_minor_valid(cmd, vg->fid->fmt, lp->major, lp->minor))
|
|
return 0;
|
|
} else {
|
|
if ((lp->minor != -1) || (lp->major != -1)) {
|
|
log_error("--major and --minor incompatible "
|
|
"with -Mn");
|
|
return 0;
|
|
}
|
|
}
|
|
} else if (arg_count(cmd, minor_ARG) || arg_count(cmd, major_ARG)) {
|
|
log_error("--major and --minor require -My");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(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_count(cmd, ignoreactivationskip_ARG))
|
|
lp->activation_skip |= ACTIVATION_SKIP_IGNORE;
|
|
|
|
if (lp->zero && (lp->activation_skip & ACTIVATION_SKIP_SET_ENABLED)
|
|
&& !(lp->activation_skip & ACTIVATION_SKIP_IGNORE)) {
|
|
log_error("--setactivationskip y requires either --zero n "
|
|
"or --ignoreactivationskip");
|
|
return 0;
|
|
}
|
|
|
|
lp->yes = arg_count(cmd, yes_ARG);
|
|
lp->force = (force_t) arg_count(cmd, force_ARG);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvcreate_params(struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp,
|
|
struct cmd_context *cmd,
|
|
int argc, char **argv)
|
|
{
|
|
int contiguous;
|
|
struct arg_value_group_list *current_group;
|
|
const char *segtype_str;
|
|
const char *tag;
|
|
|
|
memset(lp, 0, sizeof(*lp));
|
|
memset(lcp, 0, sizeof(*lcp));
|
|
dm_list_init(&lp->tags);
|
|
lp->target_attr = ~0;
|
|
|
|
/*
|
|
* Check selected options are compatible and determine segtype
|
|
*/
|
|
if ((arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG)) &&
|
|
arg_count(cmd,mirrors_ARG)) {
|
|
log_error("--thin, --thinpool and --mirrors are incompatible.");
|
|
return 0;
|
|
}
|
|
|
|
/* Set default segtype - remember, '-m 0' implies stripe. */
|
|
if (arg_count(cmd, mirrors_ARG) &&
|
|
arg_uint_value(cmd, mirrors_ARG, 0))
|
|
if (arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ?
|
|
stripes_long_ARG : stripes_ARG, 1) > 1) {
|
|
segtype_str = find_config_tree_str(cmd, global_raid10_segtype_default_CFG, NULL);;
|
|
} else {
|
|
segtype_str = find_config_tree_str(cmd, global_mirror_segtype_default_CFG, NULL);
|
|
}
|
|
else if (arg_count(cmd, thin_ARG) || arg_count(cmd, thinpool_ARG))
|
|
segtype_str = "thin";
|
|
else
|
|
segtype_str = "striped";
|
|
|
|
segtype_str = arg_str_value(cmd, type_ARG, segtype_str);
|
|
|
|
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;
|
|
}
|
|
|
|
if ((arg_count(cmd, snapshot_ARG) || seg_is_snapshot(lp)) &&
|
|
arg_count(cmd, thin_ARG)) {
|
|
log_error("--thin and --snapshot are incompatible.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, snapshot_ARG) || seg_is_snapshot(lp) ||
|
|
(!seg_is_thin(lp) && arg_count(cmd, virtualsize_ARG)))
|
|
lp->snapshot = 1;
|
|
|
|
if (seg_is_cache_pool(lp))
|
|
lp->create_pool = 1;
|
|
|
|
if (seg_is_thin_pool(lp)) {
|
|
if (lp->snapshot) {
|
|
log_error("Snapshots are incompatible with thin_pool segment_type.");
|
|
return 0;
|
|
}
|
|
lp->create_pool = 1;
|
|
}
|
|
|
|
if (seg_is_thin_volume(lp))
|
|
lp->thin = 1;
|
|
|
|
lp->mirrors = 1;
|
|
|
|
/* Default to 2 mirrored areas if '--type mirror|raid1|raid10' */
|
|
if (segtype_is_mirrored(lp->segtype))
|
|
lp->mirrors = 2;
|
|
|
|
if (arg_count(cmd, mirrors_ARG)) {
|
|
if (arg_sign_value(cmd, mirrors_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Mirrors argument may not be negative");
|
|
return 0;
|
|
}
|
|
|
|
lp->mirrors = arg_uint_value(cmd, mirrors_ARG, 0) + 1;
|
|
|
|
if (lp->mirrors > DEFAULT_MIRROR_MAX_IMAGES) {
|
|
log_error("Only up to " DM_TO_STRING(DEFAULT_MIRROR_MAX_IMAGES)
|
|
" images in mirror supported currently.");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->mirrors == 1) {
|
|
if (segtype_is_mirrored(lp->segtype)) {
|
|
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");
|
|
}
|
|
|
|
if ((lp->mirrors > 2) && !strcmp(lp->segtype->name, "raid10")) {
|
|
/*
|
|
* 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->snapshot && arg_count(cmd, zero_ARG)) {
|
|
log_error("-Z is incompatible with snapshots");
|
|
return 0;
|
|
}
|
|
|
|
if (segtype_is_mirrored(lp->segtype) || segtype_is_raid(lp->segtype)) {
|
|
if (lp->snapshot) {
|
|
log_error("mirrors and snapshots are currently "
|
|
"incompatible");
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (arg_count(cmd, corelog_ARG)) {
|
|
log_error("--corelog is only available with mirrors");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, mirrorlog_ARG)) {
|
|
log_error("--mirrorlog is only available with mirrors");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, nosync_ARG)) {
|
|
log_error("--nosync is only available with mirrors");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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 ((strcmp(lp->segtype->name, "raid10") == 0) &&
|
|
!(lp->target_attr & RAID_FEATURE_RAID10)) {
|
|
log_error("RAID module does not support RAID10.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Should we zero/wipe signatures on the lv.
|
|
*/
|
|
lp->zero = (!(lp->segtype->flags & SEG_CANNOT_BE_ZEROED) &&
|
|
(strcmp(arg_str_value(cmd, zero_ARG, "y"), "y") == 0)) ? 1 : 0;
|
|
|
|
if (arg_count(cmd, wipesignatures_ARG)) {
|
|
/* If -W/--wipesignatures is given on command line directly, respect it. */
|
|
lp->wipe_signatures =(!(lp->segtype->flags & SEG_CANNOT_BE_ZEROED) &&
|
|
(strcmp(arg_str_value(cmd, wipesignatures_ARG, "y"), "y") == 0)) ? 1 : 0;
|
|
} 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(lp, cmd, &argc, &argv) ||
|
|
!_read_size_params(lp, lcp, cmd) ||
|
|
!get_stripe_params(cmd, &lp->stripes, &lp->stripe_size) ||
|
|
(lp->create_pool &&
|
|
!get_pool_params(cmd, NULL, &lp->passed_args,
|
|
&lp->thin_chunk_size_calc_policy,
|
|
&lp->chunk_size, &lp->discards,
|
|
&lp->poolmetadatasize, &lp->zero)) ||
|
|
!_read_mirror_params(lp, cmd) ||
|
|
!_read_raid_params(lp, cmd) ||
|
|
!_read_cache_pool_params(lp, cmd))
|
|
return_0;
|
|
|
|
if (lp->snapshot && (lp->extents || lcp->size)) {
|
|
if (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 ||
|
|
(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));
|
|
|
|
if (!(lp->segtype = get_segtype_from_string(cmd, "snapshot")))
|
|
return_0;
|
|
} else if (!lp->create_pool && arg_count(cmd, chunksize_ARG)) {
|
|
log_error("--chunksize is only available with snapshots and thin pools.");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->create_pool) {
|
|
/* TODO: add lvm.conf default y|n */
|
|
lp->poolmetadataspare = arg_int_value(cmd, poolmetadataspare_ARG,
|
|
DEFAULT_POOL_METADATA_SPARE);
|
|
} else if (arg_count(cmd, poolmetadataspare_ARG)) {
|
|
log_error("--poolmetadataspare is only available with thin pool creation.");
|
|
return 0;
|
|
}
|
|
/*
|
|
* Allocation parameters
|
|
*/
|
|
contiguous = strcmp(arg_str_value(cmd, contiguous_ARG, "n"), "n");
|
|
|
|
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;
|
|
}
|
|
|
|
static int _check_thin_parameters(struct volume_group *vg, struct lvcreate_params *lp,
|
|
struct lvcreate_cmdline_params *lcp)
|
|
{
|
|
struct lv_list *lvl;
|
|
unsigned i;
|
|
|
|
if (!lp->thin && !lp->create_pool && !lp->snapshot) {
|
|
log_error("Please specify device size(s).");
|
|
return 0;
|
|
}
|
|
|
|
if (lp->thin && lp->snapshot) {
|
|
log_error("Please either create snapshot or thin volume.");
|
|
return 0;
|
|
}
|
|
|
|
if (!lp->create_pool) {
|
|
static const int _argname[] = {
|
|
alloc_ARG,
|
|
chunksize_ARG,
|
|
contiguous_ARG,
|
|
discards_ARG,
|
|
poolmetadatasize_ARG,
|
|
poolmetadataspare_ARG,
|
|
stripes_ARG,
|
|
stripesize_ARG,
|
|
zero_ARG
|
|
};
|
|
|
|
for (i = 0; i < DM_ARRAY_SIZE(_argname); ++i) {
|
|
if (arg_count(vg->cmd, _argname[i])) {
|
|
log_error("%s is only available for thin pool creation.",
|
|
arg_long_option_name(_argname[i]));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (lcp->pv_count) {
|
|
log_error("Only specify Physical volumes when allocating the thin pool.");
|
|
return 0;
|
|
}
|
|
|
|
if (!lp->pool) {
|
|
log_error("Please specify name of existing thin pool.");
|
|
return 0;
|
|
}
|
|
|
|
if (!(lvl = find_lv_in_vg(vg, lp->pool))) {
|
|
log_error("Thin pool %s not found in Volume group %s.", lp->pool, vg->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!lv_is_thin_pool(lvl->lv)) {
|
|
log_error("Logical volume %s is not a thin pool.", lp->pool);
|
|
return 0;
|
|
}
|
|
} else if (lp->pool && find_lv_in_vg(vg, lp->pool)) {
|
|
log_error("Thin pool %s already exists in Volume group %s.", lp->pool, vg->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!lp->thin && !lp->snapshot) {
|
|
if (lp->lv_name) {
|
|
log_error("--name may only be given when creating a new thin Logical volume or snapshot.");
|
|
return 0;
|
|
}
|
|
if (arg_count(vg->cmd, readahead_ARG)) {
|
|
log_error("--readhead may only be given when creating a new thin Logical volume or snapshot.");
|
|
return 0;
|
|
}
|
|
if (arg_count(vg->cmd, permission_ARG)) {
|
|
log_error("--permission may only be given when creating a new thin Logical volume or snapshot.");
|
|
return 0;
|
|
}
|
|
if (arg_count(vg->cmd, persistent_ARG)) {
|
|
log_error("--persistent may only be given when creating a new thin Logical volume or snapshot.");
|
|
return 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 (!strcmp(lp->segtype->name, "raid10")) {
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
log_error(INTERNAL_ERROR "--thinpool not identified.");
|
|
r = 0;
|
|
}
|
|
|
|
if ((lp->snapshot && !lp->origin) || (!lp->snapshot && lp->origin)) {
|
|
log_error(INTERNAL_ERROR "Inconsistent snapshot and origin parameters identified.");
|
|
r = 0;
|
|
}
|
|
|
|
if (!lp->thin && !lp->create_pool && !lp->snapshot) {
|
|
log_error(INTERNAL_ERROR "Failed to identify what type of thin target to use.");
|
|
r = 0;
|
|
}
|
|
|
|
if (seg_is_thin_pool(lp) && lp->thin) {
|
|
log_error(INTERNAL_ERROR "Thin volume cannot be created with thin pool segment type.");
|
|
r = 0;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int lvcreate(struct cmd_context *cmd, int argc, char **argv)
|
|
{
|
|
int r = ECMD_FAILED;
|
|
struct lvcreate_params lp;
|
|
struct lvcreate_cmdline_params lcp;
|
|
struct volume_group *vg;
|
|
|
|
if (!_lvcreate_params(&lp, &lcp, cmd, argc, argv))
|
|
return EINVALID_CMD_LINE;
|
|
|
|
log_verbose("Finding volume group \"%s\"", lp.vg_name);
|
|
vg = vg_read_for_update(cmd, lp.vg_name, NULL, 0);
|
|
if (vg_read_error(vg)) {
|
|
release_vg(vg);
|
|
return_ECMD_FAILED;
|
|
}
|
|
|
|
if (lp.snapshot && lp.origin && !_determine_snapshot_type(vg, &lp))
|
|
goto_out;
|
|
|
|
if (seg_is_thin(&lp) && !_check_thin_parameters(vg, &lp, &lcp))
|
|
goto_out;
|
|
|
|
if (seg_is_cache(&lp) && !_determine_cache_argument(vg, &lp))
|
|
goto_out;
|
|
|
|
if (seg_is_raid(&lp) && !_check_raid_parameters(vg, &lp, &lcp))
|
|
goto_out;
|
|
|
|
/*
|
|
* Check activation parameters to support inactive thin snapshot creation
|
|
* FIXME: anything else needs to be moved past _determine_snapshot_type()?
|
|
*/
|
|
if (!_read_activation_params(&lp, cmd, vg))
|
|
goto_out;
|
|
|
|
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.poolmetadataextents,
|
|
lp.pvh, lp.poolmetadataspare))
|
|
goto_out;
|
|
|
|
log_verbose("Making thin pool %s in VG %s using segtype %s",
|
|
lp.pool ? : "with generated name", lp.vg_name, lp.segtype->name);
|
|
}
|
|
|
|
if (lp.thin)
|
|
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 ? : "with generated name", lp.vg_name,
|
|
lp.snapshot ? " as snapshot of " : "",
|
|
lp.snapshot ? lp.origin : "", lp.segtype->name);
|
|
|
|
if (!lv_create_single(vg, &lp))
|
|
goto_out;
|
|
|
|
r = ECMD_PROCESSED;
|
|
out:
|
|
unlock_and_release_vg(cmd, vg, lp.vg_name);
|
|
return r;
|
|
}
|