mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
c9f021de0b
This avoids a problem in which we're using selection on LV list - we need to do the selection on initial state and not on any intermediary state as we process LVs one by one - some of the relations among LVs can be gone during this processing. For example, processing one LV can cause the other LVs to lose the relation to this LV and hence they're not selectable anymore with the original selection criteria as it would be if we did selection on inital state. A perfect example is with thin snapshots: $ lvs -o lv_name,origin,layout,role vg LV Origin Layout Role lvol1 thin,sparse public,origin,thinorigin,multithinorigin lvol2 lvol1 thin,sparse public,snapshot,thinsnapshot lvol3 lvol1 thin,sparse public,snapshot,thinsnapshot pool thin,pool private $ lvremove -ff -S 'lv_name=lvol1 || origin=lvol1' Logical volume "lvol1" successfully removed The lvremove command above was supposed to remove lvol1 as well as all its snapshots which have origin=lvol1. It failed to do so, because once we removed the origin lvol1, the lvol2 and lvol3 which were snapshots before are not snapshots anymore - the relations change as we're processing these LVs one by one. If we do the selection first and then execute any concrete actions on these LVs (which is what this patch does), the behaviour is correct then - the selection is done on the *initial state*: $ lvremove -ff -S 'lv_name=lvol1 || origin=lvol1' Logical volume "lvol1" successfully removed Logical volume "lvol2" successfully removed Logical volume "lvol3" successfully removed Similarly for all the other situations in which relations among LVs are being changed by processing the LVs one by one. This patch also introduces LV_REMOVED internal LV status flag to mark removed LVs so they're not processed further when we iterate over collected list of LVs to be processed. Previously, when we iterated directly over vg->lvs list to process the LVs, we relied on the fact that once the LV is removed, it is also removed from the vg->lvs list we're iterating over. But that was incorrect as we shouldn't remove LVs from the list during one iteration while we're iterating over that exact list (dm_list_iterate_items safe can handle only one removal at one iteration anyway, so it can't be used here).
210 lines
5.0 KiB
C
210 lines
5.0 KiB
C
/*
|
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2013 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 "lib.h"
|
|
#include "metadata.h"
|
|
#include "import-export.h"
|
|
#include "lvm-string.h"
|
|
|
|
/*
|
|
* Bitsets held in the 'status' flags get
|
|
* converted into arrays of strings.
|
|
*/
|
|
struct flag {
|
|
const uint64_t mask;
|
|
const char *description;
|
|
int kind;
|
|
};
|
|
|
|
static const struct flag _vg_flags[] = {
|
|
{EXPORTED_VG, "EXPORTED", STATUS_FLAG},
|
|
{RESIZEABLE_VG, "RESIZEABLE", STATUS_FLAG},
|
|
{PVMOVE, "PVMOVE", STATUS_FLAG},
|
|
{LVM_READ, "READ", STATUS_FLAG},
|
|
{LVM_WRITE, "WRITE", STATUS_FLAG},
|
|
{LVM_WRITE_LOCKED, "WRITE_LOCKED", COMPATIBLE_FLAG},
|
|
{CLUSTERED, "CLUSTERED", STATUS_FLAG},
|
|
{SHARED, "SHARED", STATUS_FLAG},
|
|
{PARTIAL_VG, NULL, 0},
|
|
{PRECOMMITTED, NULL, 0},
|
|
{ARCHIVED_VG, NULL, 0},
|
|
{0, NULL, 0}
|
|
};
|
|
|
|
static const struct flag _pv_flags[] = {
|
|
{ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG},
|
|
{EXPORTED_VG, "EXPORTED", STATUS_FLAG},
|
|
{MISSING_PV, "MISSING", COMPATIBLE_FLAG},
|
|
{UNLABELLED_PV, NULL, 0},
|
|
{0, NULL, 0}
|
|
};
|
|
|
|
static const struct flag _lv_flags[] = {
|
|
{LVM_READ, "READ", STATUS_FLAG},
|
|
{LVM_WRITE, "WRITE", STATUS_FLAG},
|
|
{LVM_WRITE_LOCKED, "WRITE_LOCKED", COMPATIBLE_FLAG},
|
|
{FIXED_MINOR, "FIXED_MINOR", STATUS_FLAG},
|
|
{VISIBLE_LV, "VISIBLE", STATUS_FLAG},
|
|
{PVMOVE, "PVMOVE", STATUS_FLAG},
|
|
{LOCKED, "LOCKED", STATUS_FLAG},
|
|
{LV_NOTSYNCED, "NOTSYNCED", STATUS_FLAG},
|
|
{LV_REBUILD, "REBUILD", STATUS_FLAG},
|
|
{LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG},
|
|
{LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG},
|
|
{LV_ERROR_WHEN_FULL, "ERROR_WHEN_FULL", COMPATIBLE_FLAG},
|
|
{LV_NOSCAN, NULL, 0},
|
|
{LV_TEMPORARY, NULL, 0},
|
|
{POOL_METADATA_SPARE, NULL, 0},
|
|
{RAID, NULL, 0},
|
|
{RAID_META, NULL, 0},
|
|
{RAID_IMAGE, NULL, 0},
|
|
{MIRROR, NULL, 0},
|
|
{MIRROR_IMAGE, NULL, 0},
|
|
{MIRROR_LOG, NULL, 0},
|
|
{MIRRORED, NULL, 0},
|
|
{VIRTUAL, NULL, 0},
|
|
{SNAPSHOT, NULL, 0},
|
|
{MERGING, NULL, 0},
|
|
{CONVERTING, NULL, 0},
|
|
{PARTIAL_LV, NULL, 0},
|
|
{POSTORDER_FLAG, NULL, 0},
|
|
{VIRTUAL_ORIGIN, NULL, 0},
|
|
{REPLICATOR, NULL, 0},
|
|
{REPLICATOR_LOG, NULL, 0},
|
|
{THIN_VOLUME, NULL, 0},
|
|
{THIN_POOL, NULL, 0},
|
|
{THIN_POOL_DATA, NULL, 0},
|
|
{THIN_POOL_METADATA, NULL, 0},
|
|
{CACHE, NULL, 0},
|
|
{CACHE_POOL, NULL, 0},
|
|
{CACHE_POOL_DATA, NULL, 0},
|
|
{CACHE_POOL_METADATA, NULL, 0},
|
|
{LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */
|
|
{LV_REMOVED, NULL, 0},
|
|
{0, NULL, 0}
|
|
};
|
|
|
|
static const struct flag *_get_flags(int type)
|
|
{
|
|
switch (type & ~STATUS_FLAG) {
|
|
case VG_FLAGS:
|
|
return _vg_flags;
|
|
|
|
case PV_FLAGS:
|
|
return _pv_flags;
|
|
|
|
case LV_FLAGS:
|
|
return _lv_flags;
|
|
}
|
|
|
|
log_error("Unknown flag set requested.");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Converts a bitset to an array of string values,
|
|
* using one of the tables defined at the top of
|
|
* the file.
|
|
*/
|
|
int print_flags(uint64_t status, int type, char *buffer, size_t size)
|
|
{
|
|
int f, first = 1;
|
|
const struct flag *flags;
|
|
|
|
if (!(flags = _get_flags(type)))
|
|
return_0;
|
|
|
|
if (!emit_to_buffer(&buffer, &size, "["))
|
|
return 0;
|
|
|
|
for (f = 0; flags[f].mask; f++) {
|
|
if (status & flags[f].mask) {
|
|
status &= ~flags[f].mask;
|
|
|
|
if ((type & STATUS_FLAG) != flags[f].kind)
|
|
continue;
|
|
|
|
/* Internal-only flag? */
|
|
if (!flags[f].description)
|
|
continue;
|
|
|
|
if (!first) {
|
|
if (!emit_to_buffer(&buffer, &size, ", "))
|
|
return 0;
|
|
} else
|
|
first = 0;
|
|
|
|
if (!emit_to_buffer(&buffer, &size, "\"%s\"",
|
|
flags[f].description))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!emit_to_buffer(&buffer, &size, "]"))
|
|
return 0;
|
|
|
|
if (status)
|
|
log_warn(INTERNAL_ERROR "Metadata inconsistency: "
|
|
"Not all flags successfully exported.");
|
|
|
|
return 1;
|
|
}
|
|
|
|
int read_flags(uint64_t *status, int type, const struct dm_config_value *cv)
|
|
{
|
|
int f;
|
|
uint64_t s = UINT64_C(0);
|
|
const struct flag *flags;
|
|
|
|
if (!(flags = _get_flags(type)))
|
|
return_0;
|
|
|
|
if (cv->type == DM_CFG_EMPTY_ARRAY)
|
|
goto out;
|
|
|
|
while (cv) {
|
|
if (cv->type != DM_CFG_STRING) {
|
|
log_error("Status value is not a string.");
|
|
return 0;
|
|
}
|
|
|
|
for (f = 0; flags[f].description; f++)
|
|
if (!strcmp(flags[f].description, cv->v.str)) {
|
|
s |= flags[f].mask;
|
|
break;
|
|
}
|
|
|
|
if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) {
|
|
/*
|
|
* Exception: We no longer write this flag out, but it
|
|
* might be encountered in old backup files, so restore
|
|
* it in that case. It is never part of live metadata
|
|
* though, so only vgcfgrestore needs to be concerned
|
|
* by this case.
|
|
*/
|
|
s |= PARTIAL_VG;
|
|
} else if (!flags[f].description && (type & STATUS_FLAG)) {
|
|
log_error("Unknown status flag '%s'.", cv->v.str);
|
|
return 0;
|
|
}
|
|
|
|
cv = cv->next;
|
|
}
|
|
|
|
out:
|
|
*status |= s;
|
|
return 1;
|
|
}
|