mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
57d74a45a0
When multiple duplicate devices are specified on the command line, the PV is processed once for each of them, but pv->dev is the device used each time. This overrides the PV device to reflect the duplicate device that was specified on the command line. This is done by hacking the lvmcache to replace pv->dev with the device of the duplicate being processed. (It would be preferable to override pv->dev without munging the content of the cache, and without sprinkling special cases throughout the code.) This override only applies when multiple duplicate devices are specified on the command line. When only a single duplicate device of pv->dev is specified, the priority is to display the cached pv->dev, so pv->dev is not overridden by the named duplicate device. In the examples below, loop3 is the cached device referenced by pv->dev, and is given priority for processing. Only after loop3 is processed/displayed, will other duplicate devices loop0/loop1 appear (when requested on the command line.) With two duplicate devices, loop0 and loop3: # pvs Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop0 PV VG Fmt Attr PSize PFree /dev/loop3 loopa lvm2 a-- 12.00m 12.00m # pvs /dev/loop3 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop0 PV VG Fmt Attr PSize PFree /dev/loop3 loopa lvm2 a-- 12.00m 12.00m # pvs /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop0 PV VG Fmt Attr PSize PFree /dev/loop3 loopa lvm2 a-- 12.00m 12.00m # pvs -o+dev_size /dev/loop0 /dev/loop3 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop0 PV VG Fmt Attr PSize PFree DevSize /dev/loop0 loopa lvm2 a-- 12.00m 12.00m 16.00m /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m With three duplicate devices, loop0, loop1, loop3: # pvs -o+dev_size Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop3 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop1 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop3 /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop0 loopa lvm2 a-- 12.00m 12.00m 16.00m /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop3 /dev/loop1 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop1 loopa lvm2 a-- 12.00m 12.00m 32.00m /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop0 /dev/loop1 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop1 loopa lvm2 a-- 12.00m 12.00m 32.00m /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m # pvs -o+dev_size /dev/loop0 /dev/loop1 /dev/loop3 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop1 not /dev/loop0 Found duplicate PV XhLbpVo0hmuwrMQLjfxuAvPFUFZqD4vr: using /dev/loop3 not /dev/loop1 PV VG Fmt Attr PSize PFree DevSize /dev/loop0 loopa lvm2 a-- 12.00m 12.00m 16.00m /dev/loop1 loopa lvm2 a-- 12.00m 12.00m 32.00m /dev/loop3 loopa lvm2 a-- 12.00m 12.00m 32.00m
2534 lines
65 KiB
C
2534 lines
65 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 <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
|
|
struct device_id_list {
|
|
struct dm_list list;
|
|
struct device *dev;
|
|
char pvid[ID_LEN + 1];
|
|
};
|
|
|
|
const char *command_name(struct cmd_context *cmd)
|
|
{
|
|
return cmd->command->name;
|
|
}
|
|
|
|
static void _sigchld_handler(int sig __attribute__((unused)))
|
|
{
|
|
while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
|
|
}
|
|
|
|
/*
|
|
* returns:
|
|
* -1 if the fork failed
|
|
* 0 if the parent
|
|
* 1 if the child
|
|
*/
|
|
int become_daemon(struct cmd_context *cmd, int skip_lvm)
|
|
{
|
|
static const char devnull[] = "/dev/null";
|
|
int null_fd;
|
|
pid_t pid;
|
|
struct sigaction act = {
|
|
{_sigchld_handler},
|
|
.sa_flags = SA_NOCLDSTOP,
|
|
};
|
|
|
|
log_verbose("Forking background process: %s", cmd->cmd_line);
|
|
|
|
sigaction(SIGCHLD, &act, NULL);
|
|
|
|
if (!skip_lvm)
|
|
sync_local_dev_names(cmd); /* Flush ops and reset dm cookie */
|
|
|
|
if ((pid = fork()) == -1) {
|
|
log_error("fork failed: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* Parent */
|
|
if (pid > 0)
|
|
return 0;
|
|
|
|
/* Child */
|
|
if (setsid() == -1)
|
|
log_error("Background process failed to setsid: %s",
|
|
strerror(errno));
|
|
|
|
/* Set this to avoid discarding output from background process */
|
|
// #define DEBUG_CHILD
|
|
|
|
#ifndef DEBUG_CHILD
|
|
if ((null_fd = open(devnull, O_RDWR)) == -1) {
|
|
log_sys_error("open", devnull);
|
|
_exit(ECMD_FAILED);
|
|
}
|
|
|
|
if ((dup2(null_fd, STDIN_FILENO) < 0) || /* reopen stdin */
|
|
(dup2(null_fd, STDOUT_FILENO) < 0) || /* reopen stdout */
|
|
(dup2(null_fd, STDERR_FILENO) < 0)) { /* reopen stderr */
|
|
log_sys_error("dup2", "redirect");
|
|
(void) close(null_fd);
|
|
_exit(ECMD_FAILED);
|
|
}
|
|
|
|
if (null_fd > STDERR_FILENO)
|
|
(void) close(null_fd);
|
|
|
|
init_verbose(VERBOSE_BASE_LEVEL);
|
|
#endif /* DEBUG_CHILD */
|
|
|
|
strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv));
|
|
|
|
if (!skip_lvm) {
|
|
reset_locking();
|
|
lvmcache_destroy(cmd, 1, 1);
|
|
if (!lvmcache_init())
|
|
/* FIXME Clean up properly here */
|
|
_exit(ECMD_FAILED);
|
|
}
|
|
dev_close_all();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Strip dev_dir if present
|
|
*/
|
|
const char *skip_dev_dir(struct cmd_context *cmd, const char *vg_name,
|
|
unsigned *dev_dir_found)
|
|
{
|
|
size_t devdir_len = strlen(cmd->dev_dir);
|
|
const char *dmdir = dm_dir() + devdir_len;
|
|
size_t dmdir_len = strlen(dmdir), vglv_sz;
|
|
char *vgname, *lvname, *layer, *vglv;
|
|
|
|
/* FIXME Do this properly */
|
|
if (*vg_name == '/')
|
|
while (vg_name[1] == '/')
|
|
vg_name++;
|
|
|
|
if (strncmp(vg_name, cmd->dev_dir, devdir_len)) {
|
|
if (dev_dir_found)
|
|
*dev_dir_found = 0;
|
|
} else {
|
|
if (dev_dir_found)
|
|
*dev_dir_found = 1;
|
|
|
|
vg_name += devdir_len;
|
|
while (*vg_name == '/')
|
|
vg_name++;
|
|
|
|
/* Reformat string if /dev/mapper found */
|
|
if (!strncmp(vg_name, dmdir, dmdir_len) && vg_name[dmdir_len] == '/') {
|
|
vg_name += dmdir_len + 1;
|
|
while (*vg_name == '/')
|
|
vg_name++;
|
|
|
|
if (!dm_split_lvm_name(cmd->mem, vg_name, &vgname, &lvname, &layer) ||
|
|
*layer) {
|
|
log_error("skip_dev_dir: Couldn't split up device name %s.",
|
|
vg_name);
|
|
return vg_name;
|
|
}
|
|
vglv_sz = strlen(vgname) + strlen(lvname) + 2;
|
|
if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
|
|
dm_snprintf(vglv, vglv_sz, "%s%s%s", vgname,
|
|
*lvname ? "/" : "",
|
|
lvname) < 0) {
|
|
log_error("vg/lv string alloc failed.");
|
|
return vg_name;
|
|
}
|
|
return vglv;
|
|
}
|
|
}
|
|
|
|
return vg_name;
|
|
}
|
|
|
|
/*
|
|
* Three possible results:
|
|
* a) return 0, skip 0: take the VG, and cmd will end in success
|
|
* b) return 0, skip 1: skip the VG, and cmd will end in success
|
|
* c) return 1, skip *: skip the VG, and cmd will end in failure
|
|
*
|
|
* Case b is the special case, and includes the following:
|
|
* . The VG is inconsistent, and the command allows for inconsistent VGs.
|
|
* . The VG is clustered, the host cannot access clustered VG's,
|
|
* and the command option has been used to ignore clustered vgs.
|
|
*
|
|
* Case c covers the other errors returned when reading the VG.
|
|
*/
|
|
int ignore_vg(struct volume_group *vg, const char *vg_name, int allow_inconsistent, int *skip)
|
|
{
|
|
uint32_t read_error = vg_read_error(vg);
|
|
*skip = 0;
|
|
|
|
if ((read_error & FAILED_INCONSISTENT) && allow_inconsistent)
|
|
read_error &= ~FAILED_INCONSISTENT; /* Check for other errors */
|
|
|
|
if ((read_error & FAILED_CLUSTERED) && vg->cmd->ignore_clustered_vgs) {
|
|
read_error &= ~FAILED_CLUSTERED; /* Check for other errors */
|
|
log_verbose("Skipping volume group %s", vg_name);
|
|
*skip = 1;
|
|
}
|
|
|
|
if (read_error != SUCCESS) {
|
|
log_error("Cannot process volume group %s", vg_name);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Metadata iteration functions
|
|
*/
|
|
int process_each_segment_in_pv(struct cmd_context *cmd,
|
|
struct volume_group *vg,
|
|
struct physical_volume *pv,
|
|
void *handle,
|
|
process_single_pvseg_fn_t process_single_pvseg)
|
|
{
|
|
struct pv_segment *pvseg;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
struct pv_segment _free_pv_segment = { .pv = pv };
|
|
|
|
if (dm_list_empty(&pv->segments)) {
|
|
ret = process_single_pvseg(cmd, NULL, &_free_pv_segment, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
} else {
|
|
dm_list_iterate_items(pvseg, &pv->segments) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
ret = process_single_pvseg(cmd, vg, pvseg, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
int process_each_segment_in_lv(struct cmd_context *cmd,
|
|
struct logical_volume *lv,
|
|
void *handle,
|
|
process_single_seg_fn_t process_single_seg)
|
|
{
|
|
struct lv_segment *seg;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
|
|
dm_list_iterate_items(seg, &lv->segments) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
ret = process_single_seg(cmd, seg, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
static const char *_extract_vgname(struct cmd_context *cmd, const char *lv_name,
|
|
const char **after)
|
|
{
|
|
const char *vg_name = lv_name;
|
|
char *st, *pos;
|
|
|
|
/* Strip dev_dir (optional) */
|
|
if (!(vg_name = skip_dev_dir(cmd, vg_name, NULL)))
|
|
return_0;
|
|
|
|
/* Require exactly one set of consecutive slashes */
|
|
if ((st = pos = strchr(vg_name, '/')))
|
|
while (*st == '/')
|
|
st++;
|
|
|
|
if (!st || strchr(st, '/')) {
|
|
log_error("\"%s\": Invalid path for Logical Volume.",
|
|
lv_name);
|
|
return 0;
|
|
}
|
|
|
|
if (!(vg_name = dm_pool_strndup(cmd->mem, vg_name, pos - vg_name))) {
|
|
log_error("Allocation of vg_name failed.");
|
|
return 0;
|
|
}
|
|
|
|
if (after)
|
|
*after = st;
|
|
|
|
return vg_name;
|
|
}
|
|
|
|
/*
|
|
* Extract default volume group name from environment
|
|
*/
|
|
static const char *_default_vgname(struct cmd_context *cmd)
|
|
{
|
|
const char *vg_path;
|
|
|
|
/* Take default VG from environment? */
|
|
vg_path = getenv("LVM_VG_NAME");
|
|
if (!vg_path)
|
|
return 0;
|
|
|
|
vg_path = skip_dev_dir(cmd, vg_path, NULL);
|
|
|
|
if (strchr(vg_path, '/')) {
|
|
log_error("\"%s\": Invalid environment var LVM_VG_NAME set for Volume Group.",
|
|
vg_path);
|
|
return 0;
|
|
}
|
|
|
|
return dm_pool_strdup(cmd->mem, vg_path);
|
|
}
|
|
|
|
/*
|
|
* Determine volume group name from a logical volume name
|
|
*/
|
|
const char *extract_vgname(struct cmd_context *cmd, const char *lv_name)
|
|
{
|
|
const char *vg_name = lv_name;
|
|
|
|
/* Path supplied? */
|
|
if (vg_name && strchr(vg_name, '/')) {
|
|
if (!(vg_name = _extract_vgname(cmd, lv_name, NULL)))
|
|
return_NULL;
|
|
|
|
return vg_name;
|
|
}
|
|
|
|
if (!(vg_name = _default_vgname(cmd))) {
|
|
if (lv_name)
|
|
log_error("Path required for Logical Volume \"%s\".",
|
|
lv_name);
|
|
return NULL;
|
|
}
|
|
|
|
return vg_name;
|
|
}
|
|
|
|
/*
|
|
* Process physical extent range specifiers
|
|
*/
|
|
static int _add_pe_range(struct dm_pool *mem, const char *pvname,
|
|
struct dm_list *pe_ranges, uint32_t start, uint32_t count)
|
|
{
|
|
struct pe_range *per;
|
|
|
|
log_debug("Adding PE range: start PE %" PRIu32 " length %" PRIu32
|
|
" on %s.", start, count, pvname);
|
|
|
|
/* Ensure no overlap with existing areas */
|
|
dm_list_iterate_items(per, pe_ranges) {
|
|
if (((start < per->start) && (start + count - 1 >= per->start)) ||
|
|
((start >= per->start) &&
|
|
(per->start + per->count - 1) >= start)) {
|
|
log_error("Overlapping PE ranges specified (%" PRIu32
|
|
"-%" PRIu32 ", %" PRIu32 "-%" PRIu32 ")"
|
|
" on %s.",
|
|
start, start + count - 1, per->start,
|
|
per->start + per->count - 1, pvname);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!(per = dm_pool_alloc(mem, sizeof(*per)))) {
|
|
log_error("Allocation of list failed.");
|
|
return 0;
|
|
}
|
|
|
|
per->start = start;
|
|
per->count = count;
|
|
dm_list_add(pe_ranges, &per->list);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _xstrtouint32(const char *s, char **p, int base, uint32_t *result)
|
|
{
|
|
unsigned long ul;
|
|
|
|
errno = 0;
|
|
ul = strtoul(s, p, base);
|
|
|
|
if (errno || *p == s || ul > UINT32_MAX)
|
|
return 0;
|
|
|
|
*result = ul;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _parse_pes(struct dm_pool *mem, char *c, struct dm_list *pe_ranges,
|
|
const char *pvname, uint32_t size)
|
|
{
|
|
char *endptr;
|
|
uint32_t start, end, len;
|
|
|
|
/* Default to whole PV */
|
|
if (!c) {
|
|
if (!_add_pe_range(mem, pvname, pe_ranges, UINT32_C(0), size))
|
|
return_0;
|
|
return 1;
|
|
}
|
|
|
|
while (*c) {
|
|
if (*c != ':')
|
|
goto error;
|
|
|
|
c++;
|
|
|
|
/* Disallow :: and :\0 */
|
|
if (*c == ':' || !*c)
|
|
goto error;
|
|
|
|
/* Default to whole range */
|
|
start = UINT32_C(0);
|
|
end = size - 1;
|
|
|
|
/* Start extent given? */
|
|
if (isdigit(*c)) {
|
|
if (!_xstrtouint32(c, &endptr, 10, &start))
|
|
goto error;
|
|
c = endptr;
|
|
/* Just one number given? */
|
|
if (!*c || *c == ':')
|
|
end = start;
|
|
}
|
|
/* Range? */
|
|
if (*c == '-') {
|
|
c++;
|
|
if (isdigit(*c)) {
|
|
if (!_xstrtouint32(c, &endptr, 10, &end))
|
|
goto error;
|
|
c = endptr;
|
|
}
|
|
} else if (*c == '+') { /* Length? */
|
|
c++;
|
|
if (isdigit(*c)) {
|
|
if (!_xstrtouint32(c, &endptr, 10, &len))
|
|
goto error;
|
|
c = endptr;
|
|
end = start + (len ? (len - 1) : 0);
|
|
}
|
|
}
|
|
|
|
if (*c && *c != ':')
|
|
goto error;
|
|
|
|
if ((start > end) || (end > size - 1)) {
|
|
log_error("PE range error: start extent %" PRIu32 " to "
|
|
"end extent %" PRIu32 ".", start, end);
|
|
return 0;
|
|
}
|
|
|
|
if (!_add_pe_range(mem, pvname, pe_ranges, start, end - start + 1))
|
|
return_0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
error:
|
|
log_error("Physical extent parsing error at %s.", c);
|
|
return 0;
|
|
}
|
|
|
|
static int _create_pv_entry(struct dm_pool *mem, struct pv_list *pvl,
|
|
char *colon, int allocatable_only, struct dm_list *r)
|
|
{
|
|
const char *pvname;
|
|
struct pv_list *new_pvl = NULL, *pvl2;
|
|
struct dm_list *pe_ranges;
|
|
|
|
pvname = pv_dev_name(pvl->pv);
|
|
if (allocatable_only && !(pvl->pv->status & ALLOCATABLE_PV)) {
|
|
log_warn("Physical volume %s not allocatable.", pvname);
|
|
return 1;
|
|
}
|
|
|
|
if (allocatable_only && is_missing_pv(pvl->pv)) {
|
|
log_warn("Physical volume %s is missing.", pvname);
|
|
return 1;
|
|
}
|
|
|
|
if (allocatable_only &&
|
|
(pvl->pv->pe_count == pvl->pv->pe_alloc_count)) {
|
|
log_warn("No free extents on physical volume \"%s\".", pvname);
|
|
return 1;
|
|
}
|
|
|
|
dm_list_iterate_items(pvl2, r)
|
|
if (pvl->pv->dev == pvl2->pv->dev) {
|
|
new_pvl = pvl2;
|
|
break;
|
|
}
|
|
|
|
if (!new_pvl) {
|
|
if (!(new_pvl = dm_pool_alloc(mem, sizeof(*new_pvl)))) {
|
|
log_error("Unable to allocate physical volume list.");
|
|
return 0;
|
|
}
|
|
|
|
memcpy(new_pvl, pvl, sizeof(*new_pvl));
|
|
|
|
if (!(pe_ranges = dm_pool_alloc(mem, sizeof(*pe_ranges)))) {
|
|
log_error("Allocation of pe_ranges list failed.");
|
|
return 0;
|
|
}
|
|
dm_list_init(pe_ranges);
|
|
new_pvl->pe_ranges = pe_ranges;
|
|
dm_list_add(r, &new_pvl->list);
|
|
}
|
|
|
|
/* Determine selected physical extents */
|
|
if (!_parse_pes(mem, colon, new_pvl->pe_ranges, pv_dev_name(pvl->pv),
|
|
pvl->pv->pe_count))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
struct dm_list *create_pv_list(struct dm_pool *mem, struct volume_group *vg, int argc,
|
|
char **argv, int allocatable_only)
|
|
{
|
|
struct dm_list *r;
|
|
struct pv_list *pvl;
|
|
struct dm_list tagsl, arg_pvnames;
|
|
char *pvname = NULL;
|
|
char *colon, *at_sign, *tagname;
|
|
int i;
|
|
|
|
/* Build up list of PVs */
|
|
if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
|
|
log_error("Allocation of list failed");
|
|
return NULL;
|
|
}
|
|
dm_list_init(r);
|
|
|
|
dm_list_init(&tagsl);
|
|
dm_list_init(&arg_pvnames);
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
dm_unescape_colons_and_at_signs(argv[i], &colon, &at_sign);
|
|
|
|
if (at_sign && (at_sign == argv[i])) {
|
|
tagname = at_sign + 1;
|
|
if (!validate_tag(tagname)) {
|
|
log_error("Skipping invalid tag %s.", tagname);
|
|
continue;
|
|
}
|
|
dm_list_iterate_items(pvl, &vg->pvs) {
|
|
if (str_list_match_item(&pvl->pv->tags,
|
|
tagname)) {
|
|
if (!_create_pv_entry(mem, pvl, NULL,
|
|
allocatable_only,
|
|
r))
|
|
return_NULL;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
pvname = argv[i];
|
|
|
|
if (colon && !(pvname = dm_pool_strndup(mem, pvname,
|
|
(unsigned) (colon - pvname)))) {
|
|
log_error("Failed to clone PV name.");
|
|
return NULL;
|
|
}
|
|
|
|
if (!(pvl = find_pv_in_vg(vg, pvname))) {
|
|
log_error("Physical Volume \"%s\" not found in "
|
|
"Volume Group \"%s\".", pvname, vg->name);
|
|
return NULL;
|
|
}
|
|
if (!_create_pv_entry(mem, pvl, colon, allocatable_only, r))
|
|
return_NULL;
|
|
}
|
|
|
|
if (dm_list_empty(r))
|
|
log_error("No specified PVs have space available.");
|
|
|
|
return dm_list_empty(r) ? NULL : r;
|
|
}
|
|
|
|
struct dm_list *clone_pv_list(struct dm_pool *mem, struct dm_list *pvsl)
|
|
{
|
|
struct dm_list *r;
|
|
struct pv_list *pvl, *new_pvl;
|
|
|
|
/* Build up list of PVs */
|
|
if (!(r = dm_pool_alloc(mem, sizeof(*r)))) {
|
|
log_error("Allocation of list failed.");
|
|
return NULL;
|
|
}
|
|
dm_list_init(r);
|
|
|
|
dm_list_iterate_items(pvl, pvsl) {
|
|
if (!(new_pvl = dm_pool_zalloc(mem, sizeof(*new_pvl)))) {
|
|
log_error("Unable to allocate physical volume list.");
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(new_pvl, pvl, sizeof(*new_pvl));
|
|
dm_list_add(r, &new_pvl->list);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
const char _pe_size_may_not_be_negative_msg[] = "Physical extent size may not be negative.";
|
|
|
|
int vgcreate_params_set_defaults(struct cmd_context *cmd,
|
|
struct vgcreate_params *vp_def,
|
|
struct volume_group *vg)
|
|
{
|
|
int64_t extent_size;
|
|
|
|
if (vg) {
|
|
vp_def->vg_name = NULL;
|
|
vp_def->extent_size = vg->extent_size;
|
|
vp_def->max_pv = vg->max_pv;
|
|
vp_def->max_lv = vg->max_lv;
|
|
vp_def->alloc = vg->alloc;
|
|
vp_def->clustered = vg_is_clustered(vg);
|
|
vp_def->vgmetadatacopies = vg->mda_copies;
|
|
} else {
|
|
vp_def->vg_name = NULL;
|
|
extent_size = find_config_tree_int64(cmd,
|
|
allocation_physical_extent_size_CFG, NULL) * 2;
|
|
if (extent_size < 0) {
|
|
log_error(_pe_size_may_not_be_negative_msg);
|
|
return 0;
|
|
}
|
|
vp_def->extent_size = (uint32_t) extent_size;
|
|
vp_def->max_pv = DEFAULT_MAX_PV;
|
|
vp_def->max_lv = DEFAULT_MAX_LV;
|
|
vp_def->alloc = DEFAULT_ALLOC_POLICY;
|
|
vp_def->clustered = DEFAULT_CLUSTERED;
|
|
vp_def->vgmetadatacopies = DEFAULT_VGMETADATACOPIES;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Set members of struct vgcreate_params from cmdline arguments.
|
|
* Do preliminary validation with arg_*() interface.
|
|
* Further, more generic validation is done in validate_vgcreate_params().
|
|
* This function is to remain in tools directory.
|
|
*/
|
|
int vgcreate_params_set_from_args(struct cmd_context *cmd,
|
|
struct vgcreate_params *vp_new,
|
|
struct vgcreate_params *vp_def)
|
|
{
|
|
vp_new->vg_name = skip_dev_dir(cmd, vp_def->vg_name, NULL);
|
|
vp_new->max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG,
|
|
vp_def->max_lv);
|
|
vp_new->max_pv = arg_uint_value(cmd, maxphysicalvolumes_ARG,
|
|
vp_def->max_pv);
|
|
vp_new->alloc = (alloc_policy_t) arg_uint_value(cmd, alloc_ARG, vp_def->alloc);
|
|
|
|
/* Units of 512-byte sectors */
|
|
vp_new->extent_size =
|
|
arg_uint_value(cmd, physicalextentsize_ARG, vp_def->extent_size);
|
|
|
|
if (arg_count(cmd, clustered_ARG))
|
|
vp_new->clustered = arg_int_value(cmd, clustered_ARG, vp_def->clustered);
|
|
else
|
|
/* Default depends on current locking type */
|
|
vp_new->clustered = locking_is_clustered();
|
|
|
|
if (arg_sign_value(cmd, physicalextentsize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error(_pe_size_may_not_be_negative_msg);
|
|
return 0;
|
|
}
|
|
|
|
if (arg_uint64_value(cmd, physicalextentsize_ARG, 0) > MAX_EXTENT_SIZE) {
|
|
log_error("Physical extent size cannot be larger than %s.",
|
|
display_size(cmd, (uint64_t) MAX_EXTENT_SIZE));
|
|
return 0;
|
|
}
|
|
|
|
if (arg_sign_value(cmd, maxlogicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Max Logical Volumes may not be negative.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_sign_value(cmd, maxphysicalvolumes_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Max Physical Volumes may not be negative.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, metadatacopies_ARG)) {
|
|
vp_new->vgmetadatacopies = arg_int_value(cmd, metadatacopies_ARG,
|
|
DEFAULT_VGMETADATACOPIES);
|
|
} else if (arg_count(cmd, vgmetadatacopies_ARG)) {
|
|
vp_new->vgmetadatacopies = arg_int_value(cmd, vgmetadatacopies_ARG,
|
|
DEFAULT_VGMETADATACOPIES);
|
|
} else {
|
|
vp_new->vgmetadatacopies = find_config_tree_int(cmd, metadata_vgmetadatacopies_CFG, NULL);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Shared code for changing activation state for vgchange/lvchange */
|
|
int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
|
|
activation_change_t activate)
|
|
{
|
|
int r = 1;
|
|
|
|
if (lv_is_cache_pool(lv)) {
|
|
if (is_change_activating(activate)) {
|
|
log_verbose("Skipping activation of cache pool %s.",
|
|
display_lvname(lv));
|
|
return 1;
|
|
}
|
|
if (!dm_list_empty(&lv->segs_using_this_lv)) {
|
|
log_verbose("Skipping deactivation of used cache pool %s.",
|
|
display_lvname(lv));
|
|
return 1;
|
|
}
|
|
/*
|
|
* Allow to pass only deactivation of unused cache pool.
|
|
* Useful only for recovery of failed zeroing of metadata LV.
|
|
*/
|
|
}
|
|
|
|
if (lv_is_merging_origin(lv)) {
|
|
/*
|
|
* For merging origin, its snapshot must be inactive.
|
|
* If it's still active and cannot be deactivated
|
|
* activation or deactivation of origin fails!
|
|
*
|
|
* When origin is deactivated and merging snapshot is thin
|
|
* it allows to deactivate origin, but still report error,
|
|
* since the thin snapshot remains active.
|
|
*
|
|
* User could retry to deactivate it with another
|
|
* deactivation of origin, which is the only visible LV
|
|
*/
|
|
if (!deactivate_lv(cmd, find_snapshot(lv)->lv)) {
|
|
if (is_change_activating(activate)) {
|
|
log_error("Refusing to activate merging \"%s\" while snapshot \"%s\" is still active.",
|
|
lv->name, find_snapshot(lv)->lv->name);
|
|
return 0;
|
|
}
|
|
|
|
log_error("Cannot fully deactivate merging origin \"%s\" while snapshot \"%s\" is still active.",
|
|
lv->name, find_snapshot(lv)->lv->name);
|
|
r = 0; /* and continue to deactivate origin... */
|
|
}
|
|
}
|
|
|
|
if (!lv_active_change(cmd, lv, activate, 0))
|
|
return_0;
|
|
|
|
if (background_polling() &&
|
|
is_change_activating(activate) &&
|
|
(lv_is_pvmove(lv) || lv_is_converting(lv) || lv_is_merging(lv)))
|
|
lv_spawn_background_polling(cmd, lv);
|
|
|
|
return r;
|
|
}
|
|
|
|
int lv_refresh(struct cmd_context *cmd, struct logical_volume *lv)
|
|
{
|
|
if (!cmd->partial_activation && (lv->status & PARTIAL_LV)) {
|
|
log_error("Refusing refresh of partial LV %s."
|
|
" Use '--activationmode partial' to override.",
|
|
lv->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!suspend_lv(cmd, lv)) {
|
|
log_error("Failed to suspend %s.", lv->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!resume_lv(cmd, lv)) {
|
|
log_error("Failed to reactivate %s.", lv->name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* check if snapshot merge should be polled
|
|
* - unfortunately: even though the dev_manager will clear
|
|
* the lv's merge attributes if a merge is not possible;
|
|
* it is clearing a different instance of the lv (as
|
|
* retrieved with lv_from_lvid)
|
|
* - fortunately: polldaemon will immediately shutdown if the
|
|
* origin doesn't have a status with a snapshot percentage
|
|
*/
|
|
if (background_polling() && lv_is_merging_origin(lv) && lv_is_active_locally(lv))
|
|
lv_spawn_background_polling(cmd, lv);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int vg_refresh_visible(struct cmd_context *cmd, struct volume_group *vg)
|
|
{
|
|
struct lv_list *lvl;
|
|
int r = 1;
|
|
|
|
sigint_allow();
|
|
dm_list_iterate_items(lvl, &vg->lvs) {
|
|
if (sigint_caught()) {
|
|
r = 0;
|
|
stack;
|
|
break;
|
|
}
|
|
|
|
if (lv_is_visible(lvl->lv) && !lv_refresh(cmd, lvl->lv)) {
|
|
r = 0;
|
|
stack;
|
|
}
|
|
}
|
|
|
|
sigint_restore();
|
|
|
|
return r;
|
|
}
|
|
|
|
void lv_spawn_background_polling(struct cmd_context *cmd,
|
|
struct logical_volume *lv)
|
|
{
|
|
const char *pvname;
|
|
|
|
if (lv_is_pvmove(lv) &&
|
|
(pvname = get_pvmove_pvname_from_lv_mirr(lv))) {
|
|
log_verbose("Spawning background pvmove process for %s.",
|
|
pvname);
|
|
pvmove_poll(cmd, pvname, 1);
|
|
} else if (lv_is_locked(lv) &&
|
|
(pvname = get_pvmove_pvname_from_lv(lv))) {
|
|
log_verbose("Spawning background pvmove process for %s.",
|
|
pvname);
|
|
pvmove_poll(cmd, pvname, 1);
|
|
}
|
|
|
|
if (lv_is_converting(lv) || lv_is_merging(lv)) {
|
|
log_verbose("Spawning background lvconvert process for %s.",
|
|
lv->name);
|
|
lvconvert_poll(cmd, lv, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Intial sanity checking of non-recovery related command-line arguments.
|
|
*
|
|
* Output arguments:
|
|
* pp: structure allocated by caller, fields written / validated here
|
|
*/
|
|
int pvcreate_params_validate(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
struct pvcreate_params *pp)
|
|
{
|
|
if (!argc) {
|
|
log_error("Please enter a physical volume path.");
|
|
return 0;
|
|
}
|
|
|
|
pp->yes = arg_count(cmd, yes_ARG);
|
|
pp->force = (force_t) arg_count(cmd, force_ARG);
|
|
|
|
if (arg_int_value(cmd, labelsector_ARG, 0) >= LABEL_SCAN_SECTORS) {
|
|
log_error("labelsector must be less than %lu.",
|
|
LABEL_SCAN_SECTORS);
|
|
return 0;
|
|
} else {
|
|
pp->labelsector = arg_int64_value(cmd, labelsector_ARG,
|
|
DEFAULT_LABELSECTOR);
|
|
}
|
|
|
|
if (!(cmd->fmt->features & FMT_MDAS) &&
|
|
(arg_count(cmd, pvmetadatacopies_ARG) ||
|
|
arg_count(cmd, metadatasize_ARG) ||
|
|
arg_count(cmd, dataalignment_ARG) ||
|
|
arg_count(cmd, dataalignmentoffset_ARG))) {
|
|
log_error("Metadata and data alignment parameters only "
|
|
"apply to text format.");
|
|
return 0;
|
|
}
|
|
|
|
if (!(cmd->fmt->features & FMT_BAS) &&
|
|
arg_count(cmd, bootloaderareasize_ARG)) {
|
|
log_error("Bootloader area parameters only "
|
|
"apply to text format.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, metadataignore_ARG))
|
|
pp->metadataignore = arg_int_value(cmd, metadataignore_ARG,
|
|
DEFAULT_PVMETADATAIGNORE);
|
|
else
|
|
pp->metadataignore = find_config_tree_bool(cmd, metadata_pvmetadataignore_CFG, NULL);
|
|
|
|
if (arg_count(cmd, pvmetadatacopies_ARG) &&
|
|
!arg_int_value(cmd, pvmetadatacopies_ARG, -1) &&
|
|
pp->metadataignore) {
|
|
log_error("metadataignore only applies to metadatacopies > 0");
|
|
return 0;
|
|
}
|
|
|
|
pp->zero = arg_int_value(cmd, zero_ARG, 1);
|
|
|
|
if (arg_sign_value(cmd, dataalignment_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Physical volume data alignment may not be negative.");
|
|
return 0;
|
|
}
|
|
pp->data_alignment = arg_uint64_value(cmd, dataalignment_ARG, UINT64_C(0));
|
|
|
|
if (pp->data_alignment > UINT32_MAX) {
|
|
log_error("Physical volume data alignment is too big.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_sign_value(cmd, dataalignmentoffset_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Physical volume data alignment offset may not be negative");
|
|
return 0;
|
|
}
|
|
pp->data_alignment_offset = arg_uint64_value(cmd, dataalignmentoffset_ARG, UINT64_C(0));
|
|
|
|
if (pp->data_alignment_offset > UINT32_MAX) {
|
|
log_error("Physical volume data alignment offset is too big.");
|
|
return 0;
|
|
}
|
|
|
|
if ((pp->data_alignment + pp->data_alignment_offset) &&
|
|
(pp->rp.pe_start != PV_PE_START_CALC)) {
|
|
if ((pp->data_alignment ? pp->rp.pe_start % pp->data_alignment : pp->rp.pe_start) != pp->data_alignment_offset) {
|
|
log_warn("WARNING: Ignoring data alignment %s"
|
|
" incompatible with restored pe_start value %s)",
|
|
display_size(cmd, pp->data_alignment + pp->data_alignment_offset),
|
|
display_size(cmd, pp->rp.pe_start));
|
|
pp->data_alignment = 0;
|
|
pp->data_alignment_offset = 0;
|
|
}
|
|
}
|
|
|
|
if (arg_sign_value(cmd, metadatasize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Metadata size may not be negative.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_sign_value(cmd, bootloaderareasize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Bootloader area size may not be negative.");
|
|
return 0;
|
|
}
|
|
|
|
pp->pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, UINT64_C(0));
|
|
if (!pp->pvmetadatasize)
|
|
pp->pvmetadatasize = find_config_tree_int(cmd, metadata_pvmetadatasize_CFG, NULL);
|
|
|
|
pp->pvmetadatacopies = arg_int_value(cmd, pvmetadatacopies_ARG, -1);
|
|
if (pp->pvmetadatacopies < 0)
|
|
pp->pvmetadatacopies = find_config_tree_int(cmd, metadata_pvmetadatacopies_CFG, NULL);
|
|
|
|
pp->rp.ba_size = arg_uint64_value(cmd, bootloaderareasize_ARG, pp->rp.ba_size);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int get_activation_monitoring_mode(struct cmd_context *cmd,
|
|
int *monitoring_mode)
|
|
{
|
|
*monitoring_mode = DEFAULT_DMEVENTD_MONITOR;
|
|
|
|
if (arg_count(cmd, monitor_ARG) &&
|
|
(arg_count(cmd, ignoremonitoring_ARG) ||
|
|
arg_count(cmd, sysinit_ARG))) {
|
|
log_error("--ignoremonitoring or --sysinit option not allowed with --monitor option.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, monitor_ARG))
|
|
*monitoring_mode = arg_int_value(cmd, monitor_ARG,
|
|
DEFAULT_DMEVENTD_MONITOR);
|
|
else if (is_static() || arg_count(cmd, ignoremonitoring_ARG) ||
|
|
arg_count(cmd, sysinit_ARG) ||
|
|
!find_config_tree_bool(cmd, activation_monitoring_CFG, NULL))
|
|
*monitoring_mode = DMEVENTD_MONITOR_IGNORE;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Read pool options from cmdline
|
|
*/
|
|
int get_pool_params(struct cmd_context *cmd,
|
|
const struct segment_type *segtype,
|
|
int *passed_args,
|
|
uint64_t *pool_metadata_size,
|
|
int *pool_metadata_spare,
|
|
uint32_t *chunk_size,
|
|
thin_discards_t *discards,
|
|
int *zero)
|
|
{
|
|
*passed_args = 0;
|
|
|
|
if (segtype_is_thin_pool(segtype) || segtype_is_thin(segtype)) {
|
|
if (arg_is_set(cmd, zero_ARG)) {
|
|
*passed_args |= PASS_ARG_ZERO;
|
|
*zero = arg_int_value(cmd, zero_ARG, 1);
|
|
log_very_verbose("%s pool zeroing.", *zero ? "Enabling" : "Disabling");
|
|
}
|
|
if (arg_is_set(cmd, discards_ARG)) {
|
|
*passed_args |= PASS_ARG_DISCARDS;
|
|
*discards = (thin_discards_t) arg_uint_value(cmd, discards_ARG, 0);
|
|
log_very_verbose("Setting pool discards to %s.",
|
|
get_pool_discards_name(*discards));
|
|
}
|
|
}
|
|
|
|
if (arg_from_list_is_negative(cmd, "may not be negative",
|
|
chunksize_ARG,
|
|
pooldatasize_ARG,
|
|
poolmetadatasize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (arg_from_list_is_zero(cmd, "may not be zero",
|
|
chunksize_ARG,
|
|
pooldatasize_ARG,
|
|
poolmetadatasize_ARG,
|
|
-1))
|
|
return_0;
|
|
|
|
if (arg_is_set(cmd, chunksize_ARG)) {
|
|
*passed_args |= PASS_ARG_CHUNK_SIZE;
|
|
*chunk_size = arg_uint_value(cmd, chunksize_ARG, 0);
|
|
|
|
if (!validate_pool_chunk_size(cmd, segtype, *chunk_size))
|
|
return_0;
|
|
|
|
log_very_verbose("Setting pool chunk size to %s.",
|
|
display_size(cmd, *chunk_size));
|
|
}
|
|
|
|
if (arg_count(cmd, poolmetadatasize_ARG)) {
|
|
if (arg_sign_value(cmd, poolmetadatasize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative pool metadata size is invalid.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, poolmetadata_ARG)) {
|
|
log_error("Please specify either metadata logical volume or its size.");
|
|
return 0;
|
|
}
|
|
|
|
*passed_args |= PASS_ARG_POOL_METADATA_SIZE;
|
|
*pool_metadata_size = arg_uint64_value(cmd, poolmetadatasize_ARG,
|
|
UINT64_C(0));
|
|
} else if (arg_count(cmd, poolmetadata_ARG))
|
|
*passed_args |= PASS_ARG_POOL_METADATA_SIZE; /* fixed size */
|
|
|
|
/* TODO: default in lvm.conf ? */
|
|
*pool_metadata_spare = arg_int_value(cmd, poolmetadataspare_ARG,
|
|
DEFAULT_POOL_METADATA_SPARE);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Generic stripe parameter checks.
|
|
*/
|
|
static int _validate_stripe_params(struct cmd_context *cmd, uint32_t *stripes,
|
|
uint32_t *stripe_size)
|
|
{
|
|
if (*stripes == 1 && *stripe_size) {
|
|
log_print_unless_silent("Ignoring stripesize argument with single stripe.");
|
|
*stripe_size = 0;
|
|
}
|
|
|
|
if (*stripes > 1 && !*stripe_size) {
|
|
*stripe_size = find_config_tree_int(cmd, metadata_stripesize_CFG, NULL) * 2;
|
|
log_print_unless_silent("Using default stripesize %s.",
|
|
display_size(cmd, (uint64_t) *stripe_size));
|
|
}
|
|
|
|
if (*stripes < 1 || *stripes > MAX_STRIPES) {
|
|
log_error("Number of stripes (%d) must be between %d and %d.",
|
|
*stripes, 1, MAX_STRIPES);
|
|
return 0;
|
|
}
|
|
|
|
if (*stripes > 1 && (*stripe_size < STRIPE_SIZE_MIN ||
|
|
*stripe_size & (*stripe_size - 1))) {
|
|
log_error("Invalid stripe size %s.",
|
|
display_size(cmd, (uint64_t) *stripe_size));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* The stripe size is limited by the size of a uint32_t, but since the
|
|
* value given by the user is doubled, and the final result must be a
|
|
* power of 2, we must divide UINT_MAX by four and add 1 (to round it
|
|
* up to the power of 2)
|
|
*/
|
|
int get_stripe_params(struct cmd_context *cmd, uint32_t *stripes, uint32_t *stripe_size)
|
|
{
|
|
/* stripes_long_ARG takes precedence (for lvconvert) */
|
|
*stripes = arg_uint_value(cmd, arg_count(cmd, stripes_long_ARG) ? stripes_long_ARG : stripes_ARG, 1);
|
|
|
|
*stripe_size = arg_uint_value(cmd, stripesize_ARG, 0);
|
|
if (*stripe_size) {
|
|
if (arg_sign_value(cmd, stripesize_ARG, SIGN_NONE) == SIGN_MINUS) {
|
|
log_error("Negative stripesize is invalid.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_uint64_value(cmd, stripesize_ARG, 0) > STRIPE_SIZE_LIMIT * 2) {
|
|
log_error("Stripe size cannot be larger than %s.",
|
|
display_size(cmd, (uint64_t) STRIPE_SIZE_LIMIT));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return _validate_stripe_params(cmd, stripes, stripe_size);
|
|
}
|
|
|
|
static int _validate_cachepool_params(struct dm_config_tree *tree)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
struct dm_config_tree *get_cachepolicy_params(struct cmd_context *cmd)
|
|
{
|
|
const char *str;
|
|
struct arg_value_group_list *group;
|
|
struct dm_config_tree *result = NULL, *prev = NULL, *current = NULL;
|
|
struct dm_config_node *cn;
|
|
int ok = 0;
|
|
|
|
dm_list_iterate_items(group, &cmd->arg_value_groups) {
|
|
if (!grouped_arg_is_set(group->arg_values, cachesettings_ARG))
|
|
continue;
|
|
|
|
current = dm_config_create();
|
|
if (!current)
|
|
goto_out;
|
|
if (prev)
|
|
current->cascade = prev;
|
|
prev = current;
|
|
|
|
if (!(str = grouped_arg_str_value(group->arg_values,
|
|
cachesettings_ARG,
|
|
NULL)))
|
|
goto_out;
|
|
|
|
if (!dm_config_parse(current, str, str + strlen(str)))
|
|
goto_out;
|
|
}
|
|
|
|
if (!(result = dm_config_flatten(current)))
|
|
goto_out;
|
|
|
|
if (!(cn = dm_config_create_node(result, "policy_settings")))
|
|
goto_out;
|
|
|
|
cn->child = result->root;
|
|
result->root = cn;
|
|
|
|
if (arg_count(cmd, cachepolicy_ARG)) {
|
|
if (!(cn = dm_config_create_node(result, "policy")))
|
|
goto_out;
|
|
|
|
result->root->sib = cn;
|
|
if (!(cn->v = dm_config_create_value(result)))
|
|
goto_out;
|
|
|
|
cn->v->type = DM_CFG_STRING;
|
|
cn->v->v.str = arg_str_value(cmd, cachepolicy_ARG, NULL);
|
|
}
|
|
|
|
if (!_validate_cachepool_params(result))
|
|
goto_out;
|
|
|
|
ok = 1;
|
|
|
|
out:
|
|
if (!ok && result) {
|
|
dm_config_destroy(result);
|
|
result = NULL;
|
|
}
|
|
while (prev) {
|
|
current = prev->cascade;
|
|
dm_config_destroy(prev);
|
|
prev = current;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* FIXME move to lib */
|
|
static int _pv_change_tag(struct physical_volume *pv, const char *tag, int addtag)
|
|
{
|
|
if (addtag) {
|
|
if (!str_list_add(pv->fmt->cmd->mem, &pv->tags, tag)) {
|
|
log_error("Failed to add tag %s to physical volume %s.",
|
|
tag, pv_dev_name(pv));
|
|
return 0;
|
|
}
|
|
} else
|
|
str_list_del(&pv->tags, tag);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Set exactly one of VG, LV or PV */
|
|
int change_tag(struct cmd_context *cmd, struct volume_group *vg,
|
|
struct logical_volume *lv, struct physical_volume *pv, int arg)
|
|
{
|
|
const char *tag;
|
|
struct arg_value_group_list *current_group;
|
|
|
|
dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
|
|
if (!grouped_arg_is_set(current_group->arg_values, arg))
|
|
continue;
|
|
|
|
if (!(tag = grouped_arg_str_value(current_group->arg_values, arg, NULL))) {
|
|
log_error("Failed to get tag.");
|
|
return 0;
|
|
}
|
|
|
|
if (vg && !vg_change_tag(vg, tag, arg == addtag_ARG))
|
|
return_0;
|
|
else if (lv && !lv_change_tag(lv, tag, arg == addtag_ARG))
|
|
return_0;
|
|
else if (pv && !_pv_change_tag(pv, tag, arg == addtag_ARG))
|
|
return_0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int process_each_label(struct cmd_context *cmd, int argc, char **argv, void *handle,
|
|
process_single_label_fn_t process_single_label)
|
|
{
|
|
struct label *label;
|
|
struct dev_iter *iter;
|
|
struct device *dev;
|
|
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
int opt = 0;
|
|
|
|
if (argc) {
|
|
for (; opt < argc; opt++) {
|
|
if (!(dev = dev_cache_get(argv[opt], cmd->full_filter))) {
|
|
log_error("Failed to find device "
|
|
"\"%s\".", argv[opt]);
|
|
ret_max = ECMD_FAILED;
|
|
continue;
|
|
}
|
|
|
|
if (!label_read(dev, &label, 0)) {
|
|
log_error("No physical volume label read from %s.",
|
|
argv[opt]);
|
|
ret_max = ECMD_FAILED;
|
|
continue;
|
|
}
|
|
|
|
ret = process_single_label(cmd, label, handle);
|
|
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
if (sigint_caught())
|
|
break;
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
if (!(iter = dev_iter_create(cmd->full_filter, 1))) {
|
|
log_error("dev_iter creation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
while ((dev = dev_iter_get(iter)))
|
|
{
|
|
if (!label_read(dev, &label, 0))
|
|
continue;
|
|
|
|
ret = process_single_label(cmd, label, handle);
|
|
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
if (sigint_caught())
|
|
break;
|
|
}
|
|
|
|
dev_iter_destroy(iter);
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* Parse persistent major minor parameters.
|
|
*
|
|
* --persistent is unspecified => state is deduced
|
|
* from presence of options --minor or --major.
|
|
*
|
|
* -Mn => --minor or --major not allowed.
|
|
*
|
|
* -My => --minor is required (and also --major on <=2.4)
|
|
*/
|
|
int get_and_validate_major_minor(const struct cmd_context *cmd,
|
|
const struct format_type *fmt,
|
|
int32_t *major, int32_t *minor)
|
|
{
|
|
if (arg_count(cmd, minor_ARG) > 1) {
|
|
log_error("Option --minor may not be repeated.");
|
|
return 0;
|
|
}
|
|
|
|
if (arg_count(cmd, major_ARG) > 1) {
|
|
log_error("Option -j|--major may not be repeated.");
|
|
return 0;
|
|
}
|
|
|
|
/* Check with default 'y' */
|
|
if (!arg_int_value(cmd, persistent_ARG, 1)) { /* -Mn */
|
|
if (arg_is_set(cmd, minor_ARG) || arg_is_set(cmd, major_ARG)) {
|
|
log_error("Options --major and --minor are incompatible with -Mn.");
|
|
return 0;
|
|
}
|
|
*major = *minor = -1;
|
|
return 1;
|
|
}
|
|
|
|
/* -1 cannot be entered as an argument for --major, --minor */
|
|
*major = arg_int_value(cmd, major_ARG, -1);
|
|
*minor = arg_int_value(cmd, minor_ARG, -1);
|
|
|
|
if (arg_is_set(cmd, persistent_ARG)) { /* -My */
|
|
if (*minor == -1) {
|
|
log_error("Please specify minor number with --minor when using -My.");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!strncmp(cmd->kernel_vsn, "2.4.", 4)) {
|
|
/* Major is required for 2.4 */
|
|
if (arg_is_set(cmd, persistent_ARG) && *major < 0) {
|
|
log_error("Please specify major number with --major when using -My.");
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (*major != -1) {
|
|
log_warn("WARNING: Ignoring supplied major number %d - "
|
|
"kernel assigns major numbers dynamically. "
|
|
"Using major number %d instead.",
|
|
*major, cmd->dev_types->device_mapper_major);
|
|
}
|
|
/* Stay with dynamic major:minor if minor is not specified. */
|
|
*major = (*minor == -1) ? -1 : cmd->dev_types->device_mapper_major;
|
|
}
|
|
|
|
if ((*minor != -1) && !validate_major_minor(cmd, fmt, *major, *minor))
|
|
return_0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Validate lvname parameter
|
|
*
|
|
* If it contains vgname, it is extracted from lvname.
|
|
* If there is passed vgname, it is compared whether its the same name.
|
|
*/
|
|
int validate_lvname_param(struct cmd_context *cmd, const char **vg_name,
|
|
const char **lv_name)
|
|
{
|
|
const char *vgname;
|
|
const char *lvname;
|
|
|
|
if (!lv_name || !*lv_name)
|
|
return 1; /* NULL lvname is ok */
|
|
|
|
/* If contains VG name, extract it. */
|
|
if (strchr(*lv_name, (int) '/')) {
|
|
if (!(vgname = _extract_vgname(cmd, *lv_name, &lvname)))
|
|
return_0;
|
|
|
|
if (!*vg_name)
|
|
*vg_name = vgname;
|
|
else if (strcmp(vgname, *vg_name)) {
|
|
log_error("Please use a single volume group name "
|
|
"(\"%s\" or \"%s\").", vgname, *vg_name);
|
|
return 0;
|
|
}
|
|
|
|
*lv_name = lvname;
|
|
}
|
|
|
|
if (!validate_name(*lv_name)) {
|
|
log_error("Logical volume name \"%s\" is invalid.",
|
|
*lv_name);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Validate lvname parameter
|
|
* This name must follow restriction rules on prefixes and suffixes.
|
|
*
|
|
* If it contains vgname, it is extracted from lvname.
|
|
* If there is passed vgname, it is compared whether its the same name.
|
|
*/
|
|
int validate_restricted_lvname_param(struct cmd_context *cmd, const char **vg_name,
|
|
const char **lv_name)
|
|
{
|
|
if (!validate_lvname_param(cmd, vg_name, lv_name))
|
|
return_0;
|
|
|
|
if (lv_name && *lv_name && !apply_lvname_restrictions(*lv_name))
|
|
return_0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
struct vgnameid_list {
|
|
struct dm_list list;
|
|
const char *vg_name;
|
|
const char *vgid;
|
|
};
|
|
|
|
/*
|
|
* Extract list of VG names and list of tags from command line arguments.
|
|
*/
|
|
static int _get_arg_vgnames(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
struct dm_list *arg_vgnames,
|
|
struct dm_list *arg_tags)
|
|
{
|
|
int opt = 0;
|
|
int ret_max = ECMD_PROCESSED;
|
|
const char *vg_name;
|
|
|
|
log_verbose("Using volume group(s) on command line.");
|
|
|
|
for (; opt < argc; opt++) {
|
|
vg_name = argv[opt];
|
|
if (*vg_name == '@') {
|
|
if (!validate_tag(vg_name + 1)) {
|
|
log_error("Skipping invalid tag: %s", vg_name);
|
|
if (ret_max < EINVALID_CMD_LINE)
|
|
ret_max = EINVALID_CMD_LINE;
|
|
continue;
|
|
}
|
|
if (!str_list_add(cmd->mem, arg_tags,
|
|
dm_pool_strdup(cmd->mem, vg_name + 1))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
vg_name = skip_dev_dir(cmd, vg_name, NULL);
|
|
if (strchr(vg_name, '/')) {
|
|
log_error("Invalid volume group name %s.", vg_name);
|
|
if (ret_max < EINVALID_CMD_LINE)
|
|
ret_max = EINVALID_CMD_LINE;
|
|
continue;
|
|
}
|
|
if (!str_list_add(cmd->mem, arg_vgnames,
|
|
dm_pool_strdup(cmd->mem, vg_name))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* FIXME Add arg to include (or not) entries with duplicate vg names?
|
|
*
|
|
* Obtain complete list of VG name/vgid pairs known on the system.
|
|
*/
|
|
static int _get_vgnameids_on_system(struct cmd_context *cmd,
|
|
struct dm_list *vgnameids_on_system,
|
|
const char *only_this_vgname, int include_internal)
|
|
{
|
|
struct vgnameid_list *vgnl;
|
|
struct dm_list *vgids;
|
|
struct dm_str_list *sl;
|
|
const char *vgid;
|
|
|
|
if (only_this_vgname) {
|
|
vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl));
|
|
if (!vgnl) {
|
|
log_error("name_id_list allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
vgnl->vg_name = dm_pool_strdup(cmd->mem, only_this_vgname);
|
|
vgnl->vgid = NULL;
|
|
|
|
dm_list_add(vgnameids_on_system, &vgnl->list);
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
log_verbose("Finding all volume groups.");
|
|
|
|
if (!lvmetad_vg_list_to_lvmcache(cmd))
|
|
stack;
|
|
|
|
/*
|
|
* Start with complete vgid list because multiple VGs might have same name.
|
|
*/
|
|
vgids = get_vgids(cmd, include_internal);
|
|
if (!vgids || dm_list_empty(vgids)) {
|
|
stack;
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
/* FIXME get_vgids() should provide these pairings directly */
|
|
dm_list_iterate_items(sl, vgids) {
|
|
if (!(vgid = sl->str))
|
|
continue;
|
|
|
|
if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
|
|
log_error("vgnameid_list allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
vgnl->vgid = dm_pool_strdup(cmd->mem, vgid);
|
|
vgnl->vg_name = lvmcache_vgname_from_vgid(cmd->mem, vgid);
|
|
|
|
dm_list_add(vgnameids_on_system, &vgnl->list);
|
|
}
|
|
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
static int _process_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
|
|
struct dm_list *vgnameids_to_process,
|
|
struct dm_list *arg_vgnames,
|
|
struct dm_list *arg_tags, void *handle,
|
|
process_single_vg_fn_t process_single_vg)
|
|
{
|
|
struct volume_group *vg;
|
|
struct vgnameid_list *vgnl;
|
|
const char *vg_name;
|
|
const char *vg_uuid;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
int skip;
|
|
int process_all = 0;
|
|
|
|
/*
|
|
* If no VG names or tags were supplied, then process all VGs.
|
|
*/
|
|
if (dm_list_empty(arg_vgnames) && dm_list_empty(arg_tags))
|
|
process_all = 1;
|
|
|
|
dm_list_iterate_items(vgnl, vgnameids_to_process) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
vg_name = vgnl->vg_name;
|
|
vg_uuid = vgnl->vgid;
|
|
skip = 0;
|
|
|
|
vg = vg_read(cmd, vg_name, vg_uuid, flags);
|
|
if (ignore_vg(vg, vg_name, flags & READ_ALLOW_INCONSISTENT, &skip)) {
|
|
stack;
|
|
ret_max = ECMD_FAILED;
|
|
release_vg(vg);
|
|
continue;
|
|
}
|
|
if (skip) {
|
|
release_vg(vg);
|
|
continue;
|
|
}
|
|
|
|
/* Process this VG? */
|
|
if (process_all ||
|
|
(!dm_list_empty(arg_vgnames) && str_list_match_item(arg_vgnames, vg_name)) ||
|
|
(!dm_list_empty(arg_tags) && str_list_match_list(arg_tags, &vg->tags, NULL))) {
|
|
ret = process_single_vg(cmd, vg_name, vg, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
|
|
if (vg_read_error(vg))
|
|
release_vg(vg);
|
|
else
|
|
unlock_and_release_vg(cmd, vg, vg_name);
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* Copy the contents of a str_list of VG names to a name list, filling
|
|
* in the vgid with NULL (unknown).
|
|
*/
|
|
static int _copy_str_to_vgnameid_list(struct cmd_context *cmd, struct dm_list *sll,
|
|
struct dm_list *vgnll)
|
|
{
|
|
const char *vgname;
|
|
struct dm_str_list *sl;
|
|
struct vgnameid_list *vgnl;
|
|
|
|
dm_list_iterate_items(sl, sll) {
|
|
vgname = sl->str;
|
|
|
|
vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl));
|
|
if (!vgnl) {
|
|
log_error("vgnameid_list allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
vgnl->vgid = NULL;
|
|
vgnl->vg_name = dm_pool_strdup(cmd->mem, vgname);
|
|
|
|
dm_list_add(vgnll, &vgnl->list);
|
|
}
|
|
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
/*
|
|
* Call process_single_vg() for each VG selected by the command line arguments.
|
|
*/
|
|
int process_each_vg(struct cmd_context *cmd, int argc, char **argv,
|
|
uint32_t flags, void *handle,
|
|
process_single_vg_fn_t process_single_vg)
|
|
{
|
|
struct dm_list arg_tags; /* str_list */
|
|
struct dm_list arg_vgnames; /* str_list */
|
|
struct dm_list vgnameids_on_system; /* vgnameid_list */
|
|
struct dm_list vgnameids_to_process; /* vgnameid_list */
|
|
|
|
int enable_all_vgs = (cmd->command->flags & ALL_VGS_IS_DEFAULT);
|
|
int ret;
|
|
|
|
dm_list_init(&arg_tags);
|
|
dm_list_init(&arg_vgnames);
|
|
dm_list_init(&vgnameids_on_system);
|
|
dm_list_init(&vgnameids_to_process);
|
|
|
|
/*
|
|
* Find any VGs or tags explicitly provided on the command line.
|
|
*/
|
|
if ((ret = _get_arg_vgnames(cmd, argc, argv, &arg_vgnames, &arg_tags)) != ECMD_PROCESSED) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Obtain the complete list of VGs present on the system if it is needed because:
|
|
* any tags were supplied and need resolving; or
|
|
* no VG names were given and the command defaults to processing all VGs.
|
|
*/
|
|
if (((dm_list_empty(&arg_vgnames) && enable_all_vgs) || !dm_list_empty(&arg_tags)) &&
|
|
((ret = _get_vgnameids_on_system(cmd, &vgnameids_on_system, NULL, 0)) != ECMD_PROCESSED)) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
if (dm_list_empty(&arg_vgnames) && dm_list_empty(&vgnameids_on_system)) {
|
|
/* FIXME Should be log_print, but suppressed for reporting cmds */
|
|
log_verbose("No volume groups found.");
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
/*
|
|
* If we obtained a full list of VGs on the system, we need to work through them all;
|
|
* otherwise we can merely work through the VG names provided.
|
|
*/
|
|
if (!dm_list_empty(&vgnameids_on_system))
|
|
dm_list_splice(&vgnameids_to_process, &vgnameids_on_system);
|
|
else if ((ret = _copy_str_to_vgnameid_list(cmd, &arg_vgnames, &vgnameids_to_process)) != ECMD_PROCESSED) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
return _process_vgnameid_list(cmd, flags, &vgnameids_to_process,
|
|
&arg_vgnames, &arg_tags, handle, process_single_vg);
|
|
}
|
|
|
|
int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
|
struct dm_list *arg_lvnames, const struct dm_list *tags_in,
|
|
int stop_on_error,
|
|
void *handle, process_single_lv_fn_t process_single_lv)
|
|
{
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret = 0;
|
|
unsigned process_all = 0;
|
|
unsigned tags_supplied = 0;
|
|
unsigned lvargs_supplied = 0;
|
|
struct lv_list *lvl;
|
|
struct dm_str_list *sl;
|
|
|
|
if (!vg_check_status(vg, EXPORTED_VG))
|
|
return_ECMD_FAILED;
|
|
|
|
if (tags_in && !dm_list_empty(tags_in))
|
|
tags_supplied = 1;
|
|
|
|
if (arg_lvnames && !dm_list_empty(arg_lvnames))
|
|
lvargs_supplied = 1;
|
|
|
|
/* Process all LVs in this VG if no restrictions given
|
|
* or if VG tags match. */
|
|
if ((!tags_supplied && !lvargs_supplied) ||
|
|
(tags_supplied && str_list_match_list(tags_in, &vg->tags, NULL)))
|
|
process_all = 1;
|
|
|
|
/*
|
|
* FIXME: In case of remove it goes through deleted entries,
|
|
* but it works since entries are allocated from vg mem pool.
|
|
*/
|
|
dm_list_iterate_items(lvl, &vg->lvs) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
if (lvl->lv->status & SNAPSHOT)
|
|
continue;
|
|
|
|
/* Skip availability change for non-virt snaps when processing all LVs */
|
|
/* FIXME: pass process_all to process_single_lv() */
|
|
if (process_all && arg_count(cmd, activate_ARG) &&
|
|
lv_is_cow(lvl->lv) && !lv_is_virtual_origin(origin_from_cow(lvl->lv)))
|
|
continue;
|
|
|
|
if (lv_is_virtual_origin(lvl->lv) && !arg_count(cmd, all_ARG)) {
|
|
if (lvargs_supplied &&
|
|
str_list_match_item(arg_lvnames, lvl->lv->name))
|
|
log_print_unless_silent("Ignoring virtual origin logical volume %s.",
|
|
display_lvname(lvl->lv));
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Only let hidden LVs through it --all was used or the LVs
|
|
* were specifically named on the command line.
|
|
*/
|
|
if (!lvargs_supplied && !lv_is_visible(lvl->lv) && !arg_count(cmd, all_ARG))
|
|
continue;
|
|
|
|
/* Only process the LV if the name matches or process_all is set or if an LV tag matches */
|
|
if (lvargs_supplied && str_list_match_item(arg_lvnames, lvl->lv->name))
|
|
/* Remove LV from list of unprocessed LV names */
|
|
str_list_del(arg_lvnames, lvl->lv->name);
|
|
else if (!process_all &&
|
|
(!tags_supplied || !str_list_match_list(tags_in, &lvl->lv->tags, NULL)))
|
|
continue;
|
|
|
|
log_very_verbose("Processing LV %s in VG %s.", lvl->lv->name, vg->name);
|
|
|
|
ret = process_single_lv(cmd, lvl->lv, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
if (stop_on_error && ret != ECMD_PROCESSED)
|
|
return ret_max;
|
|
}
|
|
|
|
if (lvargs_supplied) {
|
|
/*
|
|
* FIXME: lvm supports removal of LV with all its dependencies
|
|
* this leads to miscalculation that depends on the order of args.
|
|
*/
|
|
dm_list_iterate_items(sl, arg_lvnames) {
|
|
log_error("Failed to find logical volume \"%s/%s\"",
|
|
vg->name, sl->str);
|
|
if (ret_max < ECMD_FAILED)
|
|
ret_max = ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* If arg is tag, add it to arg_tags
|
|
* else the arg is either vgname or vgname/lvname:
|
|
* - add the vgname of each arg to arg_vgnames
|
|
* - if arg has no lvname, add just vgname arg_lvnames,
|
|
* it represents all lvs in the vg
|
|
* - if arg has lvname, add vgname/lvname to arg_lvnames
|
|
*/
|
|
static int _get_arg_lvnames(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
struct dm_list *arg_vgnames,
|
|
struct dm_list *arg_lvnames,
|
|
struct dm_list *arg_tags)
|
|
{
|
|
int opt = 0;
|
|
int ret_max = ECMD_PROCESSED;
|
|
char *vglv;
|
|
size_t vglv_sz;
|
|
const char *vgname;
|
|
const char *lv_name;
|
|
const char *tmp_lv_name;
|
|
const char *vgname_def;
|
|
unsigned dev_dir_found;
|
|
|
|
log_verbose("Using logical volume(s) on command line.");
|
|
|
|
for (; opt < argc; opt++) {
|
|
lv_name = argv[opt];
|
|
dev_dir_found = 0;
|
|
|
|
/* Do we have a tag or vgname or lvname? */
|
|
vgname = lv_name;
|
|
|
|
if (*vgname == '@') {
|
|
if (!validate_tag(vgname + 1)) {
|
|
log_error("Skipping invalid tag %s.", vgname);
|
|
continue;
|
|
}
|
|
if (!str_list_add(cmd->mem, arg_tags,
|
|
dm_pool_strdup(cmd->mem, vgname + 1))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* FIXME Jumbled parsing */
|
|
vgname = skip_dev_dir(cmd, vgname, &dev_dir_found);
|
|
|
|
if (*vgname == '/') {
|
|
log_error("\"%s\": Invalid path for Logical Volume.",
|
|
argv[opt]);
|
|
if (ret_max < ECMD_FAILED)
|
|
ret_max = ECMD_FAILED;
|
|
continue;
|
|
}
|
|
lv_name = vgname;
|
|
if ((tmp_lv_name = strchr(vgname, '/'))) {
|
|
/* Must be an LV */
|
|
lv_name = tmp_lv_name;
|
|
while (*lv_name == '/')
|
|
lv_name++;
|
|
if (!(vgname = extract_vgname(cmd, vgname))) {
|
|
if (ret_max < ECMD_FAILED) {
|
|
stack;
|
|
ret_max = ECMD_FAILED;
|
|
}
|
|
continue;
|
|
}
|
|
} else if (!dev_dir_found &&
|
|
(vgname_def = _default_vgname(cmd)))
|
|
vgname = vgname_def;
|
|
else
|
|
lv_name = NULL;
|
|
|
|
if (!str_list_add(cmd->mem, arg_vgnames,
|
|
dm_pool_strdup(cmd->mem, vgname))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
if (!lv_name) {
|
|
if (!str_list_add(cmd->mem, arg_lvnames,
|
|
dm_pool_strdup(cmd->mem, vgname))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
} else {
|
|
vglv_sz = strlen(vgname) + strlen(lv_name) + 2;
|
|
if (!(vglv = dm_pool_alloc(cmd->mem, vglv_sz)) ||
|
|
dm_snprintf(vglv, vglv_sz, "%s/%s", vgname, lv_name) < 0) {
|
|
log_error("vg/lv string alloc failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
if (!str_list_add(cmd->mem, arg_lvnames, vglv)) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t flags,
|
|
struct dm_list *vgnameids_to_process,
|
|
struct dm_list *arg_vgnames,
|
|
struct dm_list *arg_lvnames,
|
|
struct dm_list *arg_tags,
|
|
void *handle,
|
|
process_single_lv_fn_t process_single_lv)
|
|
{
|
|
struct volume_group *vg;
|
|
struct vgnameid_list *vgnl;
|
|
struct dm_str_list *sl;
|
|
struct dm_list *tags_arg;
|
|
struct dm_list lvnames;
|
|
const char *vg_name;
|
|
const char *vg_uuid;
|
|
const char *vgn;
|
|
const char *lvn;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
int skip;
|
|
|
|
dm_list_iterate_items(vgnl, vgnameids_to_process) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
vg_name = vgnl->vg_name;
|
|
vg_uuid = vgnl->vgid;
|
|
skip = 0;
|
|
|
|
/*
|
|
* arg_lvnames contains some elements that are just "vgname"
|
|
* which means process all lvs in the vg. Other elements
|
|
* are "vgname/lvname" which means process only the select
|
|
* lvs in the vg.
|
|
*/
|
|
tags_arg = arg_tags;
|
|
dm_list_init(&lvnames); /* LVs to be processed in this VG */
|
|
|
|
dm_list_iterate_items(sl, arg_lvnames) {
|
|
vgn = sl->str;
|
|
lvn = strchr(vgn, '/');
|
|
|
|
if (!lvn && !strcmp(vgn, vg_name)) {
|
|
/* Process all LVs in this VG */
|
|
tags_arg = NULL;
|
|
dm_list_init(&lvnames);
|
|
break;
|
|
}
|
|
|
|
if (lvn && !strncmp(vgn, vg_name, strlen(vg_name)) &&
|
|
strlen(vg_name) == (size_t) (lvn - vgn)) {
|
|
if (!str_list_add(cmd->mem, &lvnames,
|
|
dm_pool_strdup(cmd->mem, lvn + 1))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
vg = vg_read(cmd, vg_name, vg_uuid, flags);
|
|
if (ignore_vg(vg, vg_name, flags & READ_ALLOW_INCONSISTENT, &skip)) {
|
|
stack;
|
|
ret_max = ECMD_FAILED;
|
|
release_vg(vg);
|
|
continue;
|
|
|
|
}
|
|
if (skip) {
|
|
release_vg(vg);
|
|
continue;
|
|
}
|
|
|
|
ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
|
|
handle, process_single_lv);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
unlock_and_release_vg(cmd, vg, vg_name);
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* Call process_single_lv() for each LV selected by the command line arguments.
|
|
*/
|
|
int process_each_lv(struct cmd_context *cmd, int argc, char **argv, uint32_t flags,
|
|
void *handle, process_single_lv_fn_t process_single_lv)
|
|
{
|
|
struct dm_list arg_tags; /* str_list */
|
|
struct dm_list arg_vgnames; /* str_list */
|
|
struct dm_list arg_lvnames; /* str_list */
|
|
struct dm_list vgnameids_on_system; /* vgnameid_list */
|
|
struct dm_list vgnameids_to_process; /* vgnameid_list */
|
|
|
|
int enable_all_vgs = (cmd->command->flags & ALL_VGS_IS_DEFAULT);
|
|
int ret;
|
|
|
|
dm_list_init(&arg_tags);
|
|
dm_list_init(&arg_vgnames);
|
|
dm_list_init(&arg_lvnames);
|
|
dm_list_init(&vgnameids_on_system);
|
|
dm_list_init(&vgnameids_to_process);
|
|
|
|
/*
|
|
* Find any LVs, VGs or tags explicitly provided on the command line.
|
|
*/
|
|
if ((ret = _get_arg_lvnames(cmd, argc, argv, &arg_vgnames, &arg_lvnames, &arg_tags) != ECMD_PROCESSED)) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Obtain the complete list of VGs present on the system if it is needed because:
|
|
* any tags were supplied and need resolving; or
|
|
* no VG names were given and the command defaults to processing all VGs.
|
|
*/
|
|
if (((dm_list_empty(&arg_vgnames) && enable_all_vgs) || !dm_list_empty(&arg_tags)) &&
|
|
(ret = _get_vgnameids_on_system(cmd, &vgnameids_on_system, NULL, 0) != ECMD_PROCESSED)) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
if (dm_list_empty(&arg_vgnames) && dm_list_empty(&vgnameids_on_system)) {
|
|
/* FIXME Should be log_print, but suppressed for reporting cmds */
|
|
log_verbose("No volume groups found.");
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
/*
|
|
* If we obtained a full list of VGs on the system, we need to work through them all;
|
|
* otherwise we can merely work through the VG names provided.
|
|
*/
|
|
if (!dm_list_empty(&vgnameids_on_system))
|
|
dm_list_splice(&vgnameids_to_process, &vgnameids_on_system);
|
|
else if ((ret = _copy_str_to_vgnameid_list(cmd, &arg_vgnames, &vgnameids_to_process)) != ECMD_PROCESSED) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
return _process_lv_vgnameid_list(cmd, flags, &vgnameids_to_process, &arg_vgnames, &arg_lvnames,
|
|
&arg_tags, handle, process_single_lv);
|
|
}
|
|
|
|
static int _get_arg_pvnames(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
struct dm_list *arg_pvnames,
|
|
struct dm_list *arg_tags)
|
|
{
|
|
int opt = 0;
|
|
char *at_sign, *tagname;
|
|
char *arg_name;
|
|
int ret_max = ECMD_PROCESSED;
|
|
|
|
log_verbose("Using physical volume(s) on command line.");
|
|
|
|
for (; opt < argc; opt++) {
|
|
arg_name = argv[opt];
|
|
|
|
dm_unescape_colons_and_at_signs(arg_name, NULL, &at_sign);
|
|
if (at_sign && (at_sign == arg_name)) {
|
|
tagname = at_sign + 1;
|
|
|
|
if (!validate_tag(tagname)) {
|
|
log_error("Skipping invalid tag %s.", tagname);
|
|
if (ret_max < EINVALID_CMD_LINE)
|
|
ret_max = EINVALID_CMD_LINE;
|
|
continue;
|
|
}
|
|
if (!str_list_add(cmd->mem, arg_tags,
|
|
dm_pool_strdup(cmd->mem, tagname))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!str_list_add(cmd->mem, arg_pvnames,
|
|
dm_pool_strdup(cmd->mem, arg_name))) {
|
|
log_error("strlist allocation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
static int _get_arg_devices(struct cmd_context *cmd,
|
|
struct dm_list *arg_pvnames,
|
|
struct dm_list *arg_devices)
|
|
{
|
|
struct dm_str_list *sl;
|
|
struct device_id_list *dil;
|
|
int ret_max = ECMD_PROCESSED;
|
|
|
|
dm_list_iterate_items(sl, arg_pvnames) {
|
|
if (!(dil = dm_pool_alloc(cmd->mem, sizeof(*dil)))) {
|
|
log_error("device_id_list alloc failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
if (!(dil->dev = dev_cache_get(sl->str, cmd->filter))) {
|
|
log_error("Failed to find device for physical volume \"%s\".", sl->str);
|
|
ret_max = ECMD_FAILED;
|
|
} else {
|
|
strncpy(dil->pvid, dil->dev->pvid, ID_LEN);
|
|
dm_list_add(arg_devices, &dil->list);
|
|
}
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
static int _get_all_devices(struct cmd_context *cmd, struct dm_list *all_devices)
|
|
{
|
|
struct dev_iter *iter;
|
|
struct device *dev;
|
|
struct device_id_list *dil;
|
|
int r = ECMD_FAILED;
|
|
|
|
lvmcache_seed_infos_from_lvmetad(cmd);
|
|
|
|
if (!(iter = dev_iter_create(cmd->filter, 1))) {
|
|
log_error("dev_iter creation failed.");
|
|
return ECMD_FAILED;
|
|
}
|
|
|
|
while ((dev = dev_iter_get(iter))) {
|
|
if (!(dil = dm_pool_alloc(cmd->mem, sizeof(*dil)))) {
|
|
log_error("device_id_list alloc failed.");
|
|
goto out;
|
|
}
|
|
|
|
dil->dev = dev;
|
|
dm_list_add(all_devices, &dil->list);
|
|
}
|
|
|
|
r = ECMD_PROCESSED;
|
|
out:
|
|
dev_iter_destroy(iter);
|
|
return r;
|
|
}
|
|
|
|
static int _device_list_remove(struct dm_list *devices, struct device *dev)
|
|
{
|
|
struct device_id_list *dil;
|
|
|
|
dm_list_iterate_items(dil, devices) {
|
|
if (dil->dev == dev) {
|
|
dm_list_del(&dil->list);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct device_id_list *_device_list_find_dev(struct dm_list *devices, struct device *dev)
|
|
{
|
|
struct device_id_list *dil;
|
|
|
|
dm_list_iterate_items(dil, devices) {
|
|
if (dil->dev == dev)
|
|
return dil;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct device_id_list *_device_list_find_pvid(struct dm_list *devices, struct physical_volume *pv)
|
|
{
|
|
struct device_id_list *dil;
|
|
|
|
dm_list_iterate_items(dil, devices) {
|
|
if (id_equal((struct id *) dil->pvid, &pv->id))
|
|
return dil;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int _process_device_list(struct cmd_context *cmd, struct dm_list *all_devices,
|
|
void *handle, process_single_pv_fn_t process_single_pv)
|
|
{
|
|
struct physical_volume pv_dummy;
|
|
struct physical_volume *pv;
|
|
struct device_id_list *dil;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret = 0;
|
|
|
|
/*
|
|
* Pretend that each device is a PV with dummy values.
|
|
* FIXME Formalise this extension or find an alternative.
|
|
*/
|
|
dm_list_iterate_items(dil, all_devices) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
memset(&pv_dummy, 0, sizeof(pv_dummy));
|
|
dm_list_init(&pv_dummy.tags);
|
|
dm_list_init(&pv_dummy.segments);
|
|
pv_dummy.dev = dil->dev;
|
|
pv = &pv_dummy;
|
|
|
|
log_very_verbose("Processing device %s.", dev_name(dil->dev));
|
|
|
|
ret = process_single_pv(cmd, NULL, pv, handle);
|
|
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
|
|
return ECMD_PROCESSED;
|
|
}
|
|
|
|
static int _process_pvs_in_vg(struct cmd_context *cmd,
|
|
struct volume_group *vg,
|
|
struct dm_list *all_devices,
|
|
struct dm_list *arg_devices,
|
|
struct dm_list *arg_tags,
|
|
int process_all,
|
|
int skip,
|
|
void *handle,
|
|
process_single_pv_fn_t process_single_pv)
|
|
{
|
|
struct physical_volume *pv;
|
|
struct pv_list *pvl;
|
|
struct device_id_list *dil;
|
|
struct device *dev_orig;
|
|
const char *pv_name;
|
|
int process_pv;
|
|
int dev_found;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret = 0;
|
|
|
|
dm_list_iterate_items(pvl, &vg->pvs) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
pv = pvl->pv;
|
|
pv_name = pv_dev_name(pv);
|
|
|
|
process_pv = process_all;
|
|
|
|
/* Remove each arg_devices entry as it is processed. */
|
|
|
|
if (!process_pv && !dm_list_empty(arg_devices) &&
|
|
(dil = _device_list_find_dev(arg_devices, pv->dev))) {
|
|
process_pv = 1;
|
|
_device_list_remove(arg_devices, dil->dev);
|
|
}
|
|
|
|
/* Select the PV if the device arg has the same pvid. */
|
|
|
|
if (!process_pv && !dm_list_empty(arg_devices) &&
|
|
(dil = _device_list_find_pvid(arg_devices, pv))) {
|
|
process_pv = 1;
|
|
_device_list_remove(arg_devices, dil->dev);
|
|
}
|
|
|
|
if (!process_pv && !dm_list_empty(arg_tags) &&
|
|
str_list_match_list(arg_tags, &pv->tags, NULL))
|
|
process_pv = 1;
|
|
|
|
if (process_pv) {
|
|
if (skip)
|
|
log_verbose("Skipping PV %s in VG %s.", pv_name, vg->name);
|
|
else
|
|
log_very_verbose("Processing PV %s in VG %s.", pv_name, vg->name);
|
|
|
|
dev_found = _device_list_remove(all_devices, pv->dev);
|
|
|
|
/*
|
|
* FIXME PVs with no mdas may turn up in an orphan VG when
|
|
* not using lvmetad as well as their correct VG. They
|
|
* will be missing from all_devices the second time
|
|
* around but must not be processed twice or trigger a message.
|
|
*
|
|
* Missing PVs will also need processing even though they are
|
|
* not present in all_devices.
|
|
*/
|
|
if (!dev_found && !is_missing_pv(pv)) {
|
|
log_verbose("Skipping PV %s in VG %s: not in device list.", pv_name, vg->name);
|
|
continue;
|
|
}
|
|
|
|
if (!skip) {
|
|
ret = process_single_pv(cmd, vg, pv, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
|
|
/*
|
|
* This is a very rare and obscure case where multiple
|
|
* duplicate devices are specified on the command line
|
|
* referring to this PV. In this case we want to
|
|
* process this PV once for each specified device.
|
|
*/
|
|
|
|
if (!skip && !dm_list_empty(arg_devices)) {
|
|
while ((dil = _device_list_find_pvid(arg_devices, pv))) {
|
|
_device_list_remove(arg_devices, dil->dev);
|
|
|
|
/*
|
|
* Replace pv->dev with this dil->dev
|
|
* in lvmcache so the duplicate dev
|
|
* info will be reported. FIXME: it
|
|
* would be nicer to override pv->dev
|
|
* without munging lvmcache content.
|
|
*/
|
|
dev_orig = pv->dev;
|
|
lvmcache_replace_dev(cmd, pv, dil->dev);
|
|
|
|
log_very_verbose("Processing PV %s device %s in VG %s.",
|
|
pv_name, dev_name(dil->dev), vg->name);
|
|
|
|
ret = process_single_pv(cmd, vg, pv, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
/* Put the cache state back as it was. */
|
|
lvmcache_replace_dev(cmd, pv, dev_orig);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When processing only specific PVs, we can quit once they've all been found.
|
|
*/
|
|
if (!process_all && dm_list_empty(arg_tags) && dm_list_empty(arg_devices))
|
|
break;
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
/*
|
|
* Iterate through all PVs in each listed VG. Process a PV if
|
|
* its dev or tag matches arg_devices or arg_tags. If both
|
|
* arg_devices and arg_tags are empty, then process all PVs.
|
|
* No PV should be processed more than once.
|
|
*
|
|
* Each PV is removed from arg_devices and all_devices when it is
|
|
* processed. Any names remaining in arg_devices were not found, and
|
|
* should produce an error. Any devices remaining in all_devices were
|
|
* not found and should be processed by process_device_list().
|
|
*/
|
|
static int _process_pvs_in_vgs(struct cmd_context *cmd, uint32_t flags,
|
|
struct dm_list *all_vgnameids,
|
|
struct dm_list *all_devices,
|
|
struct dm_list *arg_devices,
|
|
struct dm_list *arg_tags,
|
|
int process_all,
|
|
void *handle,
|
|
process_single_pv_fn_t process_single_pv)
|
|
{
|
|
struct volume_group *vg;
|
|
struct vgnameid_list *vgnl;
|
|
const char *vg_name;
|
|
const char *vg_uuid;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
int skip;
|
|
|
|
dm_list_iterate_items(vgnl, all_vgnameids) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
vg_name = vgnl->vg_name;
|
|
vg_uuid = vgnl->vgid;
|
|
skip = 0;
|
|
|
|
vg = vg_read(cmd, vg_name, vg_uuid, flags | READ_WARN_INCONSISTENT);
|
|
if (ignore_vg(vg, vg_name, flags & READ_ALLOW_INCONSISTENT, &skip)) {
|
|
stack;
|
|
ret_max = ECMD_FAILED;
|
|
release_vg(vg);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Don't continue when skip is set, because we need to remove
|
|
* vg->pvs entries from devices list.
|
|
*/
|
|
|
|
ret = _process_pvs_in_vg(cmd, vg, all_devices, arg_devices, arg_tags,
|
|
process_all, skip, handle, process_single_pv);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
if (skip)
|
|
release_vg(vg);
|
|
else
|
|
unlock_and_release_vg(cmd, vg, vg->name);
|
|
|
|
/* Quit early when possible. */
|
|
if (!process_all && dm_list_empty(arg_tags) && dm_list_empty(arg_devices))
|
|
return ret_max;
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
int process_each_pv(struct cmd_context *cmd,
|
|
int argc, char **argv,
|
|
const char *only_this_vgname,
|
|
uint32_t flags,
|
|
void *handle,
|
|
process_single_pv_fn_t process_single_pv)
|
|
{
|
|
struct dm_list arg_tags; /* str_list */
|
|
struct dm_list arg_pvnames; /* str_list */
|
|
struct dm_list arg_devices; /* device_id_list */
|
|
struct dm_list all_vgnameids; /* vgnameid_list */
|
|
struct dm_list all_devices; /* device_id_list */
|
|
struct device_id_list *dil;
|
|
int process_all_pvs;
|
|
int process_all_devices;
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
|
|
dm_list_init(&arg_tags);
|
|
dm_list_init(&arg_pvnames);
|
|
dm_list_init(&arg_devices);
|
|
dm_list_init(&all_vgnameids);
|
|
dm_list_init(&all_devices);
|
|
|
|
/*
|
|
* Read all the vgs first because this has the effect of initializing
|
|
* other device/lvmcache info that is needed when creating device lists.
|
|
*/
|
|
if ((ret = _get_vgnameids_on_system(cmd, &all_vgnameids, only_this_vgname, 1) != ECMD_PROCESSED)) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Create two lists from argv:
|
|
* arg_pvnames: pvs explicitly named in argv
|
|
* arg_tags: tags explicitly named in argv
|
|
*
|
|
* Then convert arg_pvnames, which are free-form, user-specified,
|
|
* names/paths into arg_devices which can be used to match below.
|
|
*/
|
|
if ((ret = _get_arg_pvnames(cmd, argc, argv, &arg_pvnames, &arg_tags)) != ECMD_PROCESSED) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
process_all_pvs = dm_list_empty(&arg_pvnames) && dm_list_empty(&arg_tags);
|
|
|
|
process_all_devices = process_all_pvs &&
|
|
(cmd->command->flags & ENABLE_ALL_DEVS) &&
|
|
arg_count(cmd, all_ARG);
|
|
|
|
if ((ret = _get_arg_devices(cmd, &arg_pvnames, &arg_devices) != ECMD_PROCESSED)) {
|
|
/* get_arg_devices reports the error for any PV names not found. */
|
|
ret_max = ECMD_FAILED;
|
|
}
|
|
|
|
/*
|
|
* If the caller wants to process all devices (not just PVs), then all PVs
|
|
* from all VGs are processed first, removing them from all_devices. Then
|
|
* any devs remaining in all_devices are processed.
|
|
*/
|
|
if ((ret = _get_all_devices(cmd, &all_devices) != ECMD_PROCESSED)) {
|
|
stack;
|
|
return ret;
|
|
}
|
|
|
|
ret = _process_pvs_in_vgs(cmd, flags, &all_vgnameids, &all_devices,
|
|
&arg_devices, &arg_tags, process_all_pvs,
|
|
handle, process_single_pv);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
|
|
dm_list_iterate_items(dil, &arg_devices) {
|
|
log_error("Failed to find physical volume \"%s\".", dev_name(dil->dev));
|
|
ret_max = ECMD_FAILED;
|
|
}
|
|
|
|
if (!process_all_devices)
|
|
goto out;
|
|
|
|
ret = _process_device_list(cmd, &all_devices, handle, process_single_pv);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
out:
|
|
return ret_max;
|
|
}
|
|
|
|
int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
|
|
void *handle, process_single_pv_fn_t process_single_pv)
|
|
{
|
|
int ret_max = ECMD_PROCESSED;
|
|
int ret;
|
|
struct pv_list *pvl;
|
|
|
|
dm_list_iterate_items(pvl, &vg->pvs) {
|
|
if (sigint_caught())
|
|
return_ECMD_FAILED;
|
|
|
|
ret = process_single_pv(cmd, vg, pvl->pv, handle);
|
|
if (ret != ECMD_PROCESSED)
|
|
stack;
|
|
if (ret > ret_max)
|
|
ret_max = ret;
|
|
}
|
|
|
|
return ret_max;
|
|
}
|
|
|
|
int lvremove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
|
void *handle __attribute__((unused)))
|
|
{
|
|
/*
|
|
* Single force is equivalent to single --yes
|
|
* Even multiple --yes are equivalent to single --force
|
|
* When we require -ff it cannot be replaced with -f -y
|
|
*/
|
|
force_t force = (force_t) arg_count(cmd, force_ARG)
|
|
? : (arg_is_set(cmd, yes_ARG) ? DONT_PROMPT : PROMPT);
|
|
|
|
if (!lv_remove_with_dependencies(cmd, lv, force, 0))
|
|
return_ECMD_FAILED;
|
|
|
|
return ECMD_PROCESSED;
|
|
}
|