mirror of
git://sourceware.org/git/lvm2.git
synced 2025-03-06 00:58:48 +03:00
Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).
Add lvs origin_size field. Fix linux configure --enable-debug to exclude -O2. Still a few rough edges, but hopefully usable now: lvcreate -s vg1 -L 100M --virtualoriginsize 1T
This commit is contained in:
parent
7f54ef36dc
commit
87f42fda5e
@ -1,5 +1,8 @@
|
||||
Version 2.02.46 -
|
||||
================================
|
||||
Add sparse devices: lvcreate -s --virtualoriginsize (hidden zero origin).
|
||||
Add lvs origin_size field.
|
||||
Fix linux configure --enable-debug to exclude -O2.
|
||||
Implement lvconvert --repair, for repairing partially failed mirrors.
|
||||
Fix vgreduce --removemissing failure exit code.
|
||||
Fix remote metadata backup for clvmd.
|
||||
|
@ -65,6 +65,7 @@ static struct flag _lv_flags[] = {
|
||||
{CONVERTING, NULL, 0},
|
||||
{PARTIAL_LV, NULL, 0},
|
||||
{POSTORDER_FLAG, NULL, 0},
|
||||
{VIRTUAL_ORIGIN, NULL, 0},
|
||||
{0, NULL, 0}
|
||||
};
|
||||
|
||||
|
@ -77,6 +77,7 @@ struct pv_segment;
|
||||
|
||||
//#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for
|
||||
//#define POSTORDER_OPEN_FLAG 0x04000000U temporary use inside vg_read_internal. */
|
||||
//#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
|
||||
|
||||
#define LVM_READ 0x00000100U /* LV VG */
|
||||
#define LVM_WRITE 0x00000200U /* LV VG */
|
||||
@ -531,6 +532,7 @@ struct lv_segment *first_seg(const struct logical_volume *lv);
|
||||
* Useful functions for managing snapshots.
|
||||
*/
|
||||
int lv_is_origin(const struct logical_volume *lv);
|
||||
int lv_is_virtual_origin(const struct logical_volume *lv);
|
||||
int lv_is_cow(const struct logical_volume *lv);
|
||||
int lv_is_visible(const struct logical_volume *lv);
|
||||
|
||||
|
@ -81,6 +81,7 @@
|
||||
|
||||
#define POSTORDER_FLAG 0x02000000U /* Not real flags, reserved for */
|
||||
#define POSTORDER_OPEN_FLAG 0x04000000U /* temporary use inside vg_read_internal. */
|
||||
#define VIRTUAL_ORIGIN 0x08000000U /* LV - internal use only */
|
||||
|
||||
//#define LVM_READ 0x00000100U /* LV VG */
|
||||
//#define LVM_WRITE 0x00000200U /* LV VG */
|
||||
|
@ -44,6 +44,12 @@ int lv_is_displayable(const struct logical_volume *lv)
|
||||
return (lv->status & VISIBLE_LV) || lv_is_cow(lv) ? 1 : 0;
|
||||
}
|
||||
|
||||
int lv_is_virtual_origin(const struct logical_volume *lv)
|
||||
{
|
||||
return (lv->status & VIRTUAL_ORIGIN) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/* Given a cow LV, return the snapshot lv_segment that uses it */
|
||||
struct lv_segment *find_cow(const struct logical_volume *lv)
|
||||
{
|
||||
@ -105,6 +111,10 @@ int vg_add_snapshot(const char *name, struct logical_volume *origin,
|
||||
|
||||
cow->status &= ~VISIBLE_LV;
|
||||
|
||||
/* FIXME Assumes an invisible origin belongs to a sparse device */
|
||||
if (!lv_is_visible(origin))
|
||||
origin->status |= VIRTUAL_ORIGIN;
|
||||
|
||||
dm_list_add(&origin->snapshot_segs, &seg->origin_list);
|
||||
|
||||
return 1;
|
||||
|
@ -53,7 +53,7 @@
|
||||
*/
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid", "Unique identifier")
|
||||
FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid", "Unique identifier.")
|
||||
FIELD(LVS, lv, STR, "LV", lvid, 4, lvname, "lv_name", "Name. LVs created for internal use are enclosed in brackets.")
|
||||
FIELD(LVS, lv, STR, "Attr", lvid, 4, lvstatus, "lv_attr", "Various attributes - see man page.")
|
||||
FIELD(LVS, lv, NUM, "Maj", major, 3, int32, "lv_major", "Persistent major number or -1 if not persistent.")
|
||||
@ -64,11 +64,12 @@ FIELD(LVS, lv, STR, "KMin", lvid, 4, lvkmin, "lv_kernel_minor", "Currently assig
|
||||
FIELD(LVS, lv, NUM, "KRahead", lvid, 7, lvkreadahead, "lv_kernel_read_ahead", "Currently-in-use read ahead setting in current units.")
|
||||
FIELD(LVS, lv, NUM, "LSize", size, 5, size64, "lv_size", "Size of LV in current units.")
|
||||
FIELD(LVS, lv, NUM, "#Seg", lvid, 4, lvsegcount, "seg_count", "Number of segments in LV.")
|
||||
FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin", "For snapshots, the origin device of this LV")
|
||||
FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin", "For snapshots, the origin device of this LV.")
|
||||
FIELD(LVS, lv, NUM, "OSize", lvid, 5, originsize, "origin_size", "For snapshots, the size of the origin device of this LV.")
|
||||
FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, "snap_percent", "For snapshots, the percentage full if LV is active.")
|
||||
FIELD(LVS, lv, NUM, "Copy%", lvid, 6, copypercent, "copy_percent", "For mirrors and pvmove, current percentage in-sync.")
|
||||
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv", "For pvmove, Source PV of temporary LV created by pvmove")
|
||||
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, "convert_lv", "For lvconvert, Name of temporary LV created by lvconvert")
|
||||
FIELD(LVS, lv, STR, "Move", lvid, 4, movepv, "move_pv", "For pvmove, Source PV of temporary LV created by pvmove.")
|
||||
FIELD(LVS, lv, STR, "Convert", lvid, 7, convertlv, "convert_lv", "For lvconvert, Name of temporary LV created by lvconvert.")
|
||||
FIELD(LVS, lv, STR, "LV Tags", tags, 7, tags, "lv_tags", "Tags, if any.")
|
||||
FIELD(LVS, lv, STR, "Log", lvid, 3, loglv, "mirror_log", "For mirrors, the LV holding the synchronisation log.")
|
||||
FIELD(LVS, lv, STR, "Modules", lvid, 7, modules, "modules", "Kernel device-mapper modules required for this LV.")
|
||||
@ -111,7 +112,7 @@ FIELD(VGS, vg, NUM, "#VMda", cmd, 5, vgmdas, "vg_mda_count", "Number of metadata
|
||||
FIELD(VGS, vg, NUM, "VMdaFree", cmd, 9, vgmdafree, "vg_mda_free", "Free metadata area space for this VG in current units.")
|
||||
FIELD(VGS, vg, NUM, "VMdaSize", cmd, 9, vgmdasize, "vg_mda_size", "Size of smallest metadata area for this VG in current units.")
|
||||
|
||||
FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype", "Type of LV segment")
|
||||
FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype", "Type of LV segment.")
|
||||
FIELD(SEGS, seg, NUM, "#Str", area_count, 4, uint32, "stripes", "Number of stripes or mirror legs.")
|
||||
FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripesize", "For stripes, amount of data placed on one device before switching to the next.")
|
||||
FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripe_size", "For stripes, amount of data placed on one device before switching to the next.")
|
||||
|
@ -471,20 +471,6 @@ static int _segtype_disp(struct dm_report *rh __attribute((unused)),
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
{
|
||||
const struct logical_volume *lv = (const struct logical_volume *) data;
|
||||
|
||||
if (lv_is_cow(lv))
|
||||
return dm_report_field_string(rh, field,
|
||||
(const char **) &origin_from_cow(lv)->name);
|
||||
|
||||
dm_report_field_set_value(field, "", NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _loglv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
@ -537,6 +523,19 @@ static int _lvname_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _origin_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private)
|
||||
{
|
||||
const struct logical_volume *lv = (const struct logical_volume *) data;
|
||||
|
||||
if (lv_is_cow(lv))
|
||||
return _lvname_disp(rh, mem, field, origin_from_cow(lv), private);
|
||||
|
||||
dm_report_field_set_value(field, "", NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _movepv_disp(struct dm_report *rh, struct dm_pool *mem __attribute((unused)),
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private __attribute((unused)))
|
||||
@ -723,7 +722,24 @@ static int _chunksize_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
if (lv_is_cow(seg->lv))
|
||||
size = (uint64_t) find_cow(seg->lv)->chunk_size;
|
||||
else
|
||||
size = 0;
|
||||
size = UINT64_C(0);
|
||||
|
||||
return _size64_disp(rh, mem, field, &size, private);
|
||||
}
|
||||
|
||||
static int _originsize_disp(struct dm_report *rh, struct dm_pool *mem,
|
||||
struct dm_report_field *field,
|
||||
const void *data, void *private)
|
||||
{
|
||||
const struct logical_volume *lv = (const struct logical_volume *) data;
|
||||
uint64_t size;
|
||||
|
||||
if (lv_is_cow(lv))
|
||||
size = (uint64_t) find_cow(lv)->len * lv->vg->extent_size;
|
||||
else if (lv_is_origin(lv))
|
||||
size = lv->size;
|
||||
else
|
||||
size = UINT64_C(0);
|
||||
|
||||
return _size64_disp(rh, mem, field, &size, private);
|
||||
}
|
||||
|
@ -25,7 +25,9 @@ VolumeGroupName [PhysicalVolumePath...]
|
||||
{\-l|\-\-extents LogicalExtentsNumber[%{VG|FREE}] |
|
||||
\-L|\-\-size LogicalVolumeSize[kKmMgGtT]}
|
||||
[\-c|\-\-chunksize ChunkSize]
|
||||
\-s|\-\-snapshot \-n|\-\-name SnapshotLogicalVolumeName OriginalLogicalVolumePath
|
||||
\-n|\-\-name SnapshotLogicalVolumeName
|
||||
\-s|\-\-snapshot
|
||||
[OriginalLogicalVolumePath | VolumeGroupName \-\-virtualoriginsize VirtualOriginSize]
|
||||
.SH DESCRIPTION
|
||||
lvcreate creates a new logical volume in a volume group ( see
|
||||
.B vgcreate(8), vgchange(8)
|
||||
@ -144,6 +146,19 @@ to grow it. Shrinking a snapshot is supported by
|
||||
as well. Run
|
||||
.B lvdisplay(8)
|
||||
on the snapshot in order to check how much data is allocated to it.
|
||||
Note that a small amount of the space you allocate to the snapshot is
|
||||
used to track the locations of the chunks of data, so you should
|
||||
allocate slightly more space than you actually need and monitor the
|
||||
rate at which the snapshot data is growing so you can avoid running out
|
||||
of space.
|
||||
.TP
|
||||
.I \-\-virtualoriginsize VirtualOriginSize
|
||||
In conjunction with \-\-snapshot, create a sparse device of the given size
|
||||
(in MB by default). Anything written to the device will be returned when
|
||||
reading from it. Reading from other areas of the device will return
|
||||
blocks of zeros. It is implemented by creating a hidden virtual device of the
|
||||
requested size using the zero target. A suffix of _vorigin is used for
|
||||
this device.
|
||||
.TP
|
||||
.I \-Z, \-\-zero y|n
|
||||
Controls zeroing of the first KB of data in the new logical volume.
|
||||
@ -180,6 +195,11 @@ contains a file system, you can mount the snapshot logical volume on an
|
||||
arbitrary directory in order to access the contents of the filesystem to run
|
||||
a backup while the original filesystem continues to get updated.
|
||||
|
||||
"lvcreate --virtualoriginsize 1T --size 100M --snapshot --name sparse vg1"
|
||||
.br
|
||||
creates a sparse device named /dev/vg1/sparse of size 1TB with space for just
|
||||
under 100MB of actual data on it.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR lvm (8),
|
||||
.BR vgcreate (8),
|
||||
|
@ -39,7 +39,7 @@ Comma-separated ordered list of columns. Precede the list with '+' to append
|
||||
to the default selection of columns instead of replacing it. Column names are:
|
||||
lv_uuid, lv_name, lv_attr, lv_major, lv_minor, lv_kernel_major, lv_kernel_minor,
|
||||
lv_size, seg_count, origin, snap_percent,
|
||||
copy_percent, move_pv, lv_tags,
|
||||
copy_percent, move_pv, lv_tags, origin_size,
|
||||
segtype, stripes,
|
||||
stripesize, chunksize, seg_start, seg_size, seg_tags, devices,
|
||||
regionsize, mirror_log, modules.
|
||||
|
@ -58,6 +58,7 @@ arg(nameprefixes_ARG, '\0', "nameprefixes", NULL, 0)
|
||||
arg(unquoted_ARG, '\0', "unquoted", NULL, 0)
|
||||
arg(rows_ARG, '\0', "rows", NULL, 0)
|
||||
arg(dataalignment_ARG, '\0', "dataalignment", size_kb_arg, 0)
|
||||
arg(virtualoriginsize_ARG, '\0', "virtualoriginsize", size_mb_arg, 0)
|
||||
|
||||
/* Allow some variations */
|
||||
arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0)
|
||||
|
@ -163,13 +163,15 @@ xx(lvcreate,
|
||||
"\t[-t|--test]\n"
|
||||
"\t[-v|--verbose]\n"
|
||||
"\t[--version]\n"
|
||||
"\tOriginalLogicalVolume[Path] [PhysicalVolumePath...]\n\n",
|
||||
"\t[OriginalLogicalVolume[Path] |\n"
|
||||
"\t VolumeGroupName[Path] --virtualoriginsize VirtualOriginSize]]\n"
|
||||
"\t[PhysicalVolumePath...]\n\n",
|
||||
|
||||
addtag_ARG, alloc_ARG, autobackup_ARG, chunksize_ARG, contiguous_ARG,
|
||||
corelog_ARG, extents_ARG, major_ARG, minor_ARG, mirrorlog_ARG, mirrors_ARG,
|
||||
name_ARG, nosync_ARG, permission_ARG, persistent_ARG, readahead_ARG,
|
||||
regionsize_ARG, size_ARG, snapshot_ARG, stripes_ARG, stripesize_ARG,
|
||||
test_ARG, type_ARG, zero_ARG)
|
||||
test_ARG, type_ARG, virtualoriginsize_ARG, zero_ARG)
|
||||
|
||||
xx(lvdisplay,
|
||||
"Display information about a logical volume",
|
||||
|
@ -559,7 +559,8 @@ static int lvchange_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
|
||||
if (lv_is_cow(lv)) {
|
||||
if (lv_is_cow(lv) && !lv_is_virtual_origin(origin_from_cow(lv)) &&
|
||||
arg_count(cmd, available_ARG)) {
|
||||
log_error("Can't change snapshot logical volume \"%s\"",
|
||||
lv->name);
|
||||
return ECMD_FAILED;
|
||||
|
186
tools/lvcreate.c
186
tools/lvcreate.c
@ -43,6 +43,8 @@ struct lvcreate_params {
|
||||
/* size */
|
||||
uint32_t extents;
|
||||
uint64_t size;
|
||||
uint32_t voriginextents;
|
||||
uint64_t voriginsize;
|
||||
percent_t percent;
|
||||
|
||||
uint32_t permission;
|
||||
@ -64,7 +66,7 @@ static int _lvcreate_name_params(struct lvcreate_params *lp,
|
||||
if (arg_count(cmd, name_ARG))
|
||||
lp->lv_name = arg_value(cmd, name_ARG);
|
||||
|
||||
if (lp->snapshot) {
|
||||
if (lp->snapshot && !arg_count(cmd, virtualoriginsize_ARG)) {
|
||||
if (!argc) {
|
||||
log_err("Please specify a logical volume to act as "
|
||||
"the snapshot origin.");
|
||||
@ -175,6 +177,20 @@ static int _read_size_params(struct lvcreate_params *lp,
|
||||
lp->percent = PERCENT_NONE;
|
||||
}
|
||||
|
||||
/* Size returned in kilobyte units; held in sectors */
|
||||
if (arg_count(cmd, virtualoriginsize_ARG)) {
|
||||
if (arg_sign_value(cmd, virtualoriginsize_ARG, 0) == SIGN_MINUS) {
|
||||
log_error("Negative virtual origin size is invalid");
|
||||
return 0;
|
||||
}
|
||||
lp->voriginsize = arg_uint64_value(cmd, virtualoriginsize_ARG,
|
||||
UINT64_C(0));
|
||||
if (!lp->voriginsize) {
|
||||
log_error("Virtual origin size may not be zero");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -390,6 +406,10 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
|
||||
log_error("-c is only available with snapshots");
|
||||
return 0;
|
||||
}
|
||||
if (arg_count(cmd, virtualoriginsize_ARG)) {
|
||||
log_error("--virtualoriginsize is only available with snapshots");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->mirrors > 1) {
|
||||
@ -511,12 +531,73 @@ static int _lvcreate_params(struct lvcreate_params *lp, struct cmd_context *cmd,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint64_t _extents_from_size(struct cmd_context *cmd, uint64_t size,
|
||||
uint32_t extent_size)
|
||||
{
|
||||
if (size % extent_size) {
|
||||
size += extent_size - size % extent_size;
|
||||
log_print("Rounding up size to full physical extent %s",
|
||||
display_size(cmd, size));
|
||||
}
|
||||
|
||||
if (size > (uint64_t) UINT32_MAX * extent_size) {
|
||||
log_error("Volume too large (%s) for extent size %s. "
|
||||
"Upper limit is %s.",
|
||||
display_size(cmd, size),
|
||||
display_size(cmd, (uint64_t) extent_size),
|
||||
display_size(cmd, (uint64_t) UINT32_MAX *
|
||||
extent_size));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint64_t) size / extent_size;
|
||||
}
|
||||
|
||||
static struct logical_volume *_create_virtual_origin(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
const char *lv_name,
|
||||
uint32_t permission,
|
||||
uint64_t voriginextents)
|
||||
{
|
||||
const struct segment_type *segtype;
|
||||
size_t len;
|
||||
char *vorigin_name;
|
||||
struct logical_volume *lv;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(cmd, "zero"))) {
|
||||
log_error("Zero segment type for virtual origin not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = strlen(lv_name) + 32;
|
||||
if (!(vorigin_name = alloca(len)) ||
|
||||
dm_snprintf(vorigin_name, len, "%s_vorigin", lv_name) < 0) {
|
||||
log_error("Virtual origin name allocation failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(lv = lv_create_empty(vorigin_name, NULL, permission,
|
||||
ALLOC_INHERIT, 0, vg)))
|
||||
return_0;
|
||||
|
||||
if (!lv_extend(lv, segtype, 1, 0, 1, voriginextents, NULL, 0u, 0u,
|
||||
NULL, ALLOC_INHERIT))
|
||||
return_0;
|
||||
|
||||
/* store vg on disk(s) */
|
||||
if (!vg_write(vg) || !vg_commit(vg))
|
||||
return_0;
|
||||
|
||||
backup(vg);
|
||||
|
||||
return lv;
|
||||
}
|
||||
|
||||
static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
struct lvcreate_params *lp)
|
||||
{
|
||||
uint32_t size_rest;
|
||||
uint32_t status = 0;
|
||||
uint64_t tmp_size;
|
||||
struct logical_volume *lv, *org = NULL;
|
||||
struct dm_list *pvh;
|
||||
const char *tag = NULL;
|
||||
@ -562,28 +643,14 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lp->size) {
|
||||
/* No of 512-byte sectors */
|
||||
tmp_size = lp->size;
|
||||
if (lp->size &&
|
||||
!(lp->extents = _extents_from_size(cmd, lp->size, vg->extent_size)))
|
||||
return_0;
|
||||
|
||||
if (tmp_size % vg->extent_size) {
|
||||
tmp_size += vg->extent_size - tmp_size %
|
||||
vg->extent_size;
|
||||
log_print("Rounding up size to full physical extent %s",
|
||||
display_size(cmd, tmp_size));
|
||||
}
|
||||
|
||||
if (tmp_size > (uint64_t) UINT32_MAX * vg->extent_size) {
|
||||
log_error("Volume too large (%s) for extent size %s. "
|
||||
"Upper limit is %s.",
|
||||
display_size(cmd, tmp_size),
|
||||
display_size(cmd, (uint64_t) vg->extent_size),
|
||||
display_size(cmd, (uint64_t) UINT32_MAX *
|
||||
vg->extent_size));
|
||||
return 0;
|
||||
}
|
||||
lp->extents = (uint64_t) tmp_size / vg->extent_size;
|
||||
}
|
||||
if (lp->voriginsize &&
|
||||
!(lp->voriginextents = _extents_from_size(cmd, lp->voriginsize,
|
||||
vg->extent_size)))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* Create the pv list.
|
||||
@ -645,37 +712,49 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
log_error("Clustered snapshots are not yet supported.");
|
||||
return 0;
|
||||
}
|
||||
if (!(org = find_lv(vg, lp->origin))) {
|
||||
log_err("Couldn't find origin volume '%s'.",
|
||||
lp->origin);
|
||||
return 0;
|
||||
}
|
||||
if (lv_is_cow(org)) {
|
||||
log_error("Snapshots of snapshots are not supported "
|
||||
"yet.");
|
||||
return 0;
|
||||
}
|
||||
if (org->status & LOCKED) {
|
||||
log_error("Snapshots of locked devices are not "
|
||||
"supported yet");
|
||||
return 0;
|
||||
}
|
||||
if (org->status & MIRROR_IMAGE ||
|
||||
org->status & MIRROR_LOG ||
|
||||
org->status & MIRRORED) {
|
||||
log_error("Snapshots and mirrors may not yet be mixed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Must zero cow */
|
||||
status |= LVM_WRITE;
|
||||
|
||||
if (!lv_info(cmd, org, &info, 0, 0)) {
|
||||
log_error("Check for existence of snapshot origin "
|
||||
"'%s' failed.", org->name);
|
||||
return 0;
|
||||
if (arg_count(cmd, virtualoriginsize_ARG))
|
||||
origin_active = 1;
|
||||
else {
|
||||
|
||||
if (!(org = find_lv(vg, lp->origin))) {
|
||||
log_error("Couldn't find origin volume '%s'.",
|
||||
lp->origin);
|
||||
return 0;
|
||||
}
|
||||
if (lv_is_virtual_origin(lv)) {
|
||||
log_error("Can't share virtual origins. "
|
||||
"Use --virtualoriginsize.");
|
||||
return 0;
|
||||
}
|
||||
if (lv_is_cow(org)) {
|
||||
log_error("Snapshots of snapshots are not "
|
||||
"supported yet.");
|
||||
return 0;
|
||||
}
|
||||
if (org->status & LOCKED) {
|
||||
log_error("Snapshots of locked devices are not "
|
||||
"supported yet");
|
||||
return 0;
|
||||
}
|
||||
if (org->status & MIRROR_IMAGE ||
|
||||
org->status & MIRROR_LOG ||
|
||||
org->status & MIRRORED) {
|
||||
log_error("Snapshots and mirrors may not yet "
|
||||
"be mixed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_info(cmd, org, &info, 0, 0)) {
|
||||
log_error("Check for existence of snapshot "
|
||||
"origin '%s' failed.", org->name);
|
||||
return 0;
|
||||
}
|
||||
origin_active = info.exists;
|
||||
}
|
||||
origin_active = info.exists;
|
||||
}
|
||||
|
||||
if (!lp->extents) {
|
||||
@ -828,6 +907,15 @@ static int _lvcreate(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lp->voriginsize &&
|
||||
!(org = _create_virtual_origin(cmd, vg, lv->name,
|
||||
lp->permission,
|
||||
lp->voriginextents))) {
|
||||
log_error("Couldn't create virtual origin for LV %s",
|
||||
lv->name);
|
||||
goto deactivate_and_revert_new_lv;
|
||||
}
|
||||
|
||||
/* cow LV remains active and becomes snapshot LV */
|
||||
|
||||
if (!vg_add_snapshot(NULL, org, lv, NULL,
|
||||
|
@ -18,6 +18,14 @@
|
||||
static int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
void *handle __attribute((unused)))
|
||||
{
|
||||
struct logical_volume *origin;
|
||||
|
||||
/*
|
||||
* If this is a sparse device, remove its origin too.
|
||||
*/
|
||||
if (lv_is_cow(lv) && lv_is_virtual_origin(origin = origin_from_cow(lv)))
|
||||
lv = origin;
|
||||
|
||||
if (!lv_remove_with_dependencies(cmd, lv, arg_count(cmd, force_ARG)))
|
||||
return ECMD_FAILED;
|
||||
|
||||
|
@ -1229,6 +1229,12 @@ int apply_lvname_restrictions(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strstr(name, "_vorigin")) {
|
||||
log_error("Names including \"_vorigin\" are reserved. "
|
||||
"Please choose a different LV name.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user