mirror of
git://sourceware.org/git/lvm2.git
synced 2026-02-10 08:32:46 +03:00
Compare commits
20 Commits
dev-dct-ap
...
dev-dct-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6029808eac | ||
|
|
b078703302 | ||
|
|
fa9eb76a5d | ||
|
|
b59127a838 | ||
|
|
d14a8040d4 | ||
|
|
ae029fcced | ||
|
|
7b04ed07ba | ||
|
|
5410dd5441 | ||
|
|
be61bd6ff5 | ||
|
|
74a211cfd3 | ||
|
|
bb41ca86fa | ||
|
|
53803821de | ||
|
|
b5811b7c9c | ||
|
|
70a45c44e8 | ||
|
|
d3b515cea5 | ||
|
|
deb5160181 | ||
|
|
16da6651a1 | ||
|
|
cf74123830 | ||
|
|
ce8277b47e | ||
|
|
33fdeaf3f1 |
@@ -1,7 +1,7 @@
|
||||
#ifndef BASE_DATA_STRUCT_LIST_H
|
||||
#define BASE_DATA_STRUCT_LIST_H
|
||||
|
||||
#include <stddef.h> /* offsetof */
|
||||
#include "base/memory/container_of.h"
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
@@ -100,7 +100,7 @@ struct dm_list *dm_list_next(const struct dm_list *head, const struct dm_list *e
|
||||
* contained in a structure of type t, return the containing structure.
|
||||
*/
|
||||
#define dm_list_struct_base(v, t, head) \
|
||||
((t *)((const char *)(v) - offsetof(t, head)))
|
||||
container_of(v, t, head)
|
||||
|
||||
/*
|
||||
* Given the address v of an instance of 'struct dm_list list' contained in
|
||||
|
||||
20
configure.ac
20
configure.ac
@@ -1047,26 +1047,6 @@ if test "$NOTIFYDBUS_SUPPORT" = yes; then
|
||||
PKG_CHECK_MODULES(NOTIFY_DBUS, systemd >= 221, [HAVE_NOTIFY_DBUS=yes], $bailout)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build appmachineid
|
||||
AC_MSG_CHECKING(whether to build appmachineid)
|
||||
AC_ARG_ENABLE(app-machineid,
|
||||
AC_HELP_STRING([--enable-app-machineid],
|
||||
[enable LVM system ID using app-specific machine-id]),
|
||||
APP_MACHINEID_SUPPORT=$enableval, APP_MACHINEID_SUPPORT=no)
|
||||
AC_MSG_RESULT($APP_MACHINEID_SUPPORT)
|
||||
|
||||
if test "$APP_MACHINEID_SUPPORT" = yes; then
|
||||
AC_DEFINE([APP_MACHINEID_SUPPORT], 1, [Define to 1 to include code that uses libsystemd machine-id apis.])
|
||||
SYSTEMD_LIBS="-lsystemd"
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Look for libsystemd libraries
|
||||
if test "$APP_MACHINEID_SUPPORT" = yes; then
|
||||
PKG_CHECK_MODULES(APP_MACHINEID, systemd >= 234, [HAVE_APP_MACHINEID=yes], $bailout)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
|
||||
dnl -- Enable blkid wiping functionality
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef _LVM_LVMLOCKD_INTERNAL_H
|
||||
#define _LVM_LVMLOCKD_INTERNAL_H
|
||||
|
||||
#include "base/memory/container_of.h"
|
||||
|
||||
#define MAX_NAME 64
|
||||
#define MAX_ARGS 64
|
||||
|
||||
@@ -217,10 +219,6 @@ struct val_blk {
|
||||
/* lm_unlock flags */
|
||||
#define LMUF_FREE_VG 0x00000001
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
|
||||
@@ -29,6 +29,7 @@ SOURCES =\
|
||||
device/bcache.c \
|
||||
device/bcache-utils.c \
|
||||
device/dev-cache.c \
|
||||
device/device_id.c \
|
||||
device/dev-ext.c \
|
||||
device/dev-io.c \
|
||||
device/dev-md.c \
|
||||
@@ -52,6 +53,7 @@ SOURCES =\
|
||||
filters/filter-usable.c \
|
||||
filters/filter-internal.c \
|
||||
filters/filter-signature.c \
|
||||
filters/filter-deviceid.c \
|
||||
format_text/archive.c \
|
||||
format_text/archiver.c \
|
||||
format_text/export.c \
|
||||
|
||||
4
lib/cache/lvmcache.c
vendored
4
lib/cache/lvmcache.c
vendored
@@ -84,6 +84,7 @@ static DM_LIST_INIT(_unused_duplicates);
|
||||
static DM_LIST_INIT(_prev_unused_duplicate_devs);
|
||||
static int _vgs_locked = 0;
|
||||
static int _found_duplicate_vgnames = 0;
|
||||
static int _outdated_warning = 0;
|
||||
|
||||
int lvmcache_init(struct cmd_context *cmd)
|
||||
{
|
||||
@@ -1776,6 +1777,9 @@ int lvmcache_update_vg_from_read(struct volume_group *vg, unsigned precommitted)
|
||||
log_warn("WARNING: outdated PV %s seqno %u has been removed in current VG %s seqno %u.",
|
||||
dev_name(info->dev), info->summary_seqno, vg->name, vginfo->seqno);
|
||||
|
||||
if (!_outdated_warning++)
|
||||
log_warn("See vgck --updatemetadata to clear outdated metadata.");
|
||||
|
||||
_drop_vginfo(info, vginfo); /* remove from vginfo->infos */
|
||||
dm_list_add(&vginfo->outdated_infos, &info->list);
|
||||
}
|
||||
|
||||
@@ -40,10 +40,6 @@
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef APP_MACHINEID_SUPPORT
|
||||
#include <systemd/sd-id128.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
@@ -132,12 +128,9 @@ static const char *_read_system_id_from_file(struct cmd_context *cmd, const char
|
||||
return system_id;
|
||||
}
|
||||
|
||||
/* systemd-id128 new produced: f64406832c2140e8ac5422d1089aae03 */
|
||||
#define LVM_APPLICATION_ID SD_ID128_MAKE(f6,44,06,83,2c,21,40,e8,ac,54,22,d1,08,9a,ae,03)
|
||||
|
||||
static const char *_system_id_from_source(struct cmd_context *cmd, const char *source)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
char filebuf[PATH_MAX];
|
||||
const char *file;
|
||||
const char *etc_str;
|
||||
const char *str;
|
||||
@@ -156,23 +149,10 @@ static const char *_system_id_from_source(struct cmd_context *cmd, const char *s
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef APP_MACHINEID_SUPPORT
|
||||
if (!strcasecmp(source, "appmachineid")) {
|
||||
sd_id128_t id;
|
||||
|
||||
sd_id128_get_machine_app_specific(LVM_APPLICATION_ID, &id);
|
||||
|
||||
if (dm_snprintf(buf, PATH_MAX, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0)
|
||||
stack;
|
||||
system_id = system_id_from_string(cmd, buf);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!strcasecmp(source, "machineid") || !strcasecmp(source, "machine-id")) {
|
||||
etc_str = find_config_tree_str(cmd, global_etc_CFG, NULL);
|
||||
if (dm_snprintf(buf, sizeof(buf), "%s/machine-id", etc_str) != -1)
|
||||
system_id = _read_system_id_from_file(cmd, buf);
|
||||
if (dm_snprintf(filebuf, sizeof(filebuf), "%s/machine-id", etc_str) != -1)
|
||||
system_id = _read_system_id_from_file(cmd, filebuf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1086,7 +1066,14 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_FILTERS 10
|
||||
static int _init_device_ids(struct cmd_context *cmd)
|
||||
{
|
||||
dm_list_init(&cmd->use_device_ids);
|
||||
cmd->devices_file = find_config_tree_str(cmd, devices_devicesfile_CFG, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_FILTERS 11
|
||||
|
||||
static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
|
||||
{
|
||||
@@ -1105,6 +1092,9 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
|
||||
* sysfs filter. Only available on 2.6 kernels. Non-critical.
|
||||
* Listed first because it's very efficient at eliminating
|
||||
* unavailable devices.
|
||||
*
|
||||
* TODO: I suspect that using the lvm_type and device_id
|
||||
* filters before this one may be more efficient.
|
||||
*/
|
||||
if (find_config_tree_bool(cmd, devices_sysfs_scan_CFG, NULL)) {
|
||||
if ((filters[nr_filt] = sysfs_filter_create()))
|
||||
@@ -1143,6 +1133,13 @@ static struct dev_filter *_init_filter_chain(struct cmd_context *cmd)
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/* filter based on the device_ids saved in the lvm_devices file */
|
||||
if (!(filters[nr_filt] = deviceid_filter_create(cmd))) {
|
||||
log_error("Failed to create deviceid device filter");
|
||||
goto bad;
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/* usable device filter. Required. */
|
||||
if (!(filters[nr_filt] = usable_filter_create(cmd, cmd->dev_types, FILTER_MODE_NO_LVMETAD))) {
|
||||
log_error("Failed to create usabled device filter");
|
||||
@@ -1737,6 +1734,9 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
if (!_init_dev_cache(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!_init_device_ids(cmd))
|
||||
return_0;
|
||||
|
||||
memlock_init(cmd);
|
||||
|
||||
if (!_init_formats(cmd))
|
||||
@@ -1941,6 +1941,9 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
if (!_init_dev_cache(cmd))
|
||||
return_0;
|
||||
|
||||
if (!_init_device_ids(cmd))
|
||||
return_0;
|
||||
|
||||
if (!_init_formats(cmd))
|
||||
return_0;
|
||||
|
||||
|
||||
@@ -182,13 +182,16 @@ struct cmd_context {
|
||||
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
|
||||
unsigned scan_lvs:1;
|
||||
unsigned wipe_outdated_pvs:1;
|
||||
unsigned enable_device_ids:1;
|
||||
|
||||
/*
|
||||
* Devices and filtering.
|
||||
*/
|
||||
struct dev_filter *filter;
|
||||
struct dm_list hints;
|
||||
struct dm_list use_device_ids;
|
||||
const char *md_component_checks;
|
||||
const char *devices_file;
|
||||
|
||||
/*
|
||||
* Configuration.
|
||||
|
||||
@@ -288,6 +288,9 @@ cfg_array(devices_preferred_names_CFG, "preferred_names", devices_CFG_SECTION, C
|
||||
"preferred_names = [ \"^/dev/mpath/\", \"^/dev/mapper/mpath\", \"^/dev/[hs]d\" ]\n"
|
||||
"#\n")
|
||||
|
||||
cfg(devices_devicesfile_CFG, "devicesfile", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_DEVICES_FILE, vsn(2, 3, 10), NULL, 0, NULL,
|
||||
"The file listing devices LVM should use.\n")
|
||||
|
||||
cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, "#Sa|.*|", vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Limit the block devices that are used by LVM commands.\n"
|
||||
"This is a list of regular expressions used to accept or reject block\n"
|
||||
@@ -1224,12 +1227,10 @@ cfg(global_system_id_source_CFG, "system_id_source", global_CFG_SECTION, 0, CFG_
|
||||
" uname\n"
|
||||
" Set the system ID from the hostname (uname) of the system.\n"
|
||||
" System IDs beginning localhost are not permitted.\n"
|
||||
" appmachineid\n"
|
||||
" Use an LVM-specific derivation of the local machine-id as the\n"
|
||||
" system ID. See 'man machine-id'.\n"
|
||||
" machineid\n"
|
||||
" Use the contents of the machine-id file to set the system ID\n"
|
||||
" (appmachineid is recommended.)\n"
|
||||
" Use the contents of the machine-id file to set the system ID.\n"
|
||||
" Some systems create this file at installation time.\n"
|
||||
" See 'man machine-id' and global/etc.\n"
|
||||
" file\n"
|
||||
" Use the contents of another file (system_id_file) to set the\n"
|
||||
" system ID.\n"
|
||||
|
||||
@@ -320,4 +320,6 @@
|
||||
|
||||
#define DEFAULT_MD_COMPONENT_CHECKS "auto"
|
||||
|
||||
#define DEFAULT_DEVICES_FILE "/etc/lvm/lvm_devices.dat"
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/device/dev-type.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/datastruct/btree.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
@@ -65,12 +66,14 @@ static int _insert(const char *path, const struct stat *info,
|
||||
static void _dev_init(struct device *dev)
|
||||
{
|
||||
dev->fd = -1;
|
||||
dev->bcache_fd = -1;
|
||||
dev->read_ahead = -1;
|
||||
|
||||
dev->ext.enabled = 0;
|
||||
dev->ext.src = DEV_EXT_NONE;
|
||||
|
||||
dm_list_init(&dev->aliases);
|
||||
dm_list_init(&dev->ids);
|
||||
}
|
||||
|
||||
void dev_destroy_file(struct device *dev)
|
||||
@@ -350,7 +353,7 @@ static int _add_alias(struct device *dev, const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value)
|
||||
int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t len;
|
||||
@@ -391,7 +394,7 @@ static int _get_dm_uuid_from_sysfs(char *buf, size_t buf_size, int major, int mi
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _get_sysfs_value(path, buf, buf_size, 0);
|
||||
return get_sysfs_value(path, buf, buf_size, 0);
|
||||
}
|
||||
|
||||
static struct dm_list *_get_or_add_list_by_index_key(struct dm_hash_table *idx, const char *key)
|
||||
@@ -472,7 +475,7 @@ static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *dev
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_get_sysfs_value(path, buf, sizeof(buf), 1))
|
||||
if (!get_sysfs_value(path, buf, sizeof(buf), 1))
|
||||
return_NULL;
|
||||
|
||||
if (sscanf(buf, "%d:%d", &major, &minor) != 2) {
|
||||
@@ -970,7 +973,7 @@ static int _dev_cache_iterate_sysfs_for_index(const char *path)
|
||||
return r;
|
||||
}
|
||||
|
||||
int dev_cache_index_devs(void)
|
||||
static int dev_cache_index_devs(void)
|
||||
{
|
||||
static int sysfs_has_dev_block = -1;
|
||||
char path[PATH_MAX];
|
||||
@@ -1319,12 +1322,19 @@ int dev_cache_check_for_open_devices(void)
|
||||
|
||||
int dev_cache_exit(void)
|
||||
{
|
||||
struct device *dev;
|
||||
struct dm_hash_node *n;
|
||||
int num_open = 0;
|
||||
|
||||
if (_cache.names)
|
||||
if ((num_open = _check_for_open_devices(1)) > 0)
|
||||
log_error(INTERNAL_ERROR "%d device(s) were left open and have been closed.", num_open);
|
||||
|
||||
dm_hash_iterate(n, _cache.names) {
|
||||
dev = (struct device *) dm_hash_get_data(_cache.names, n);
|
||||
free_dids(&dev->ids);
|
||||
}
|
||||
|
||||
if (_cache.mem)
|
||||
dm_pool_destroy(_cache.mem);
|
||||
|
||||
@@ -1656,3 +1666,38 @@ bool dev_cache_has_md_with_end_superblock(struct dev_types *dt)
|
||||
return false;
|
||||
}
|
||||
|
||||
void setup_devices(struct cmd_context *cmd)
|
||||
{
|
||||
/*
|
||||
* Read the list of device ids that lvm can use.
|
||||
* Adds a struct dev_id to cmd->use_device_ids for each one.
|
||||
*
|
||||
* (Changing enable_device_ids=0 must ensure that nothing
|
||||
* in the command thus far has been done on the basis of
|
||||
* enable_device_ids=1.)
|
||||
*/
|
||||
if (!device_ids_read(cmd)) {
|
||||
log_warn("WARNING: disabling use of devices_file, failed to read file.");
|
||||
cmd->enable_device_ids = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a 'struct device' to dev-cache for each device available on the system.
|
||||
* This will not open or read any devices, but may look at sysfs properties.
|
||||
* This list of devs comes from looking /dev entries, or from asking libudev.
|
||||
* TODO: or from /proc/partitions?
|
||||
*
|
||||
* TODO: dev_cache_scan() optimization: start by looking only at
|
||||
* devnames listed in the devices_file, and if the device_ids for
|
||||
* those all match we won't need any others.
|
||||
* Exceptions: the command wants a new device for pvcreate, or
|
||||
* device_ids don't match the devnames.
|
||||
*/
|
||||
dev_cache_scan();
|
||||
|
||||
/*
|
||||
* Match entries from cmd->use_device_ids with device structs in dev-cache.
|
||||
*/
|
||||
device_ids_match(cmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ struct dev_filter {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
int dev_cache_index_devs(void);
|
||||
struct dm_list *dev_cache_get_dev_list_for_vgid(const char *vgid);
|
||||
struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid);
|
||||
|
||||
@@ -74,4 +73,8 @@ void dev_cache_failed_path(struct device *dev, const char *path);
|
||||
|
||||
bool dev_cache_has_md_with_end_superblock(struct dev_types *dt);
|
||||
|
||||
int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value);
|
||||
|
||||
void setup_devices(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#define DEV_SCAN_FOUND_LABEL 0x00010000 /* label scan read dev and found label */
|
||||
#define DEV_IS_MD_COMPONENT 0x00020000 /* device is an md component */
|
||||
#define DEV_UDEV_INFO_MISSING 0x00040000 /* we have no udev info for this device */
|
||||
#define DEV_MATCHED_USE_ID 0x00080000 /* matched an entry from cmd->use_device_ids */
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
@@ -56,12 +57,40 @@ struct dev_ext {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
#define DEV_ID_TYPE_SYS_WWID 0x0001
|
||||
#define DEV_ID_TYPE_SYS_SERIAL 0x0002
|
||||
#define DEV_ID_TYPE_MPATH_UUID 0x0003
|
||||
#define DEV_ID_TYPE_DEVNAME 0x0004
|
||||
#define DEV_ID_TYPE_LOOP_FILE 0x0005
|
||||
|
||||
/* A device ID of a certain type for a device. */
|
||||
|
||||
struct dev_id {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
uint16_t idtype;
|
||||
char *idname;
|
||||
};
|
||||
|
||||
/* A device listed in devices_file that lvm should use. */
|
||||
|
||||
struct use_id {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
uint16_t idtype;
|
||||
char *idname;
|
||||
char *devname;
|
||||
char *pvid;
|
||||
};
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct dm_list aliases; /* struct dm_str_list */
|
||||
struct dm_list ids; /* struct dev_id */
|
||||
struct dev_id *id; /* points to the ids entry being used for this dev */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
|
||||
925
lib/device/device_id.c
Normal file
925
lib/device/device_id.c
Normal file
@@ -0,0 +1,925 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
#include "lib/device/device.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lib/device/dev-type.h"
|
||||
#include "lib/device/device-types.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
void free_uid(struct use_id *uid)
|
||||
{
|
||||
if (uid->idname)
|
||||
free(uid->idname);
|
||||
if (uid->devname)
|
||||
free(uid->devname);
|
||||
if (uid->pvid)
|
||||
free(uid->pvid);
|
||||
free(uid);
|
||||
}
|
||||
|
||||
void free_uids(struct dm_list *uids)
|
||||
{
|
||||
struct use_id *uid, *safe;
|
||||
|
||||
dm_list_iterate_items_safe(uid, safe, uids) {
|
||||
dm_list_del(&uid->list);
|
||||
free_uid(uid);
|
||||
}
|
||||
}
|
||||
|
||||
void free_did(struct dev_id *did)
|
||||
{
|
||||
if (did->idname)
|
||||
free(did->idname);
|
||||
free(did);
|
||||
}
|
||||
|
||||
void free_dids(struct dm_list *dids)
|
||||
{
|
||||
struct dev_id *did, *safe;
|
||||
|
||||
dm_list_iterate_items_safe(did, safe, dids) {
|
||||
dm_list_del(&did->list);
|
||||
free_did(did);
|
||||
}
|
||||
}
|
||||
|
||||
static int _read_sys_wwid(struct cmd_context *cmd, struct device *dev, char **idname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[PATH_MAX] = { 0 };
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/device/wwid",
|
||||
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_sysfs_value(path, buf, sizeof(buf), 0);
|
||||
|
||||
if (buf[0]) {
|
||||
if (!(*idname = strdup(buf)))
|
||||
return 0;
|
||||
} else {
|
||||
*idname = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _read_sys_serial(struct cmd_context *cmd, struct device *dev, char **idname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[PATH_MAX] = { 0 };
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/device/serial",
|
||||
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_sysfs_value(path, buf, sizeof(buf), 0);
|
||||
|
||||
if (buf[0]) {
|
||||
if (!(*idname = strdup(buf)))
|
||||
return 0;
|
||||
} else {
|
||||
*idname = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* the dm uuid uses the wwid of the underlying dev */
|
||||
|
||||
static int _read_mpath_uuid(struct cmd_context *cmd, struct device *dev, char **idname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[PATH_MAX] = { 0 };
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/dm/uuid",
|
||||
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_sysfs_value(path, buf, sizeof(buf), 0);
|
||||
|
||||
if (buf[0]) {
|
||||
if (!(*idname = strdup(buf)))
|
||||
return 0;
|
||||
} else {
|
||||
*idname = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, char **idname)
|
||||
{
|
||||
if (MAJOR(dev->dev) != cmd->dev_types->device_mapper_major)
|
||||
return 0;
|
||||
|
||||
_read_mpath_uuid(cmd, dev, idname);
|
||||
|
||||
if (*idname)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _read_loop_file(struct cmd_context *cmd, struct device *dev, char **idname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[PATH_MAX] = { 0 };
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/loop/backing_file",
|
||||
dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_sysfs_value(path, buf, sizeof(buf), 0);
|
||||
|
||||
if (buf[0]) {
|
||||
if (!(*idname = strdup(buf)))
|
||||
return 0;
|
||||
} else {
|
||||
*idname = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: should there be a list like lvm.conf
|
||||
* device_id_types = [ "sys_wwid", "sys_serial" ]
|
||||
* that controls which idtype's will be used?
|
||||
*
|
||||
* TODO: add a type for md devices, probably have it
|
||||
* use the uuid from the md dev superblock. This would
|
||||
* help in case of inconsistent md dev names, but would
|
||||
* not help in case md components were all cloned.
|
||||
*/
|
||||
static char *_device_id_system_read(struct cmd_context *cmd, struct device *dev, uint16_t idtype)
|
||||
{
|
||||
char *idname = NULL;
|
||||
|
||||
if (idtype == DEV_ID_TYPE_SYS_WWID)
|
||||
_read_sys_wwid(cmd, dev, &idname);
|
||||
|
||||
else if (idtype == DEV_ID_TYPE_SYS_SERIAL)
|
||||
_read_sys_serial(cmd, dev, &idname);
|
||||
|
||||
else if (idtype == DEV_ID_TYPE_DEVNAME)
|
||||
idname = strdup(dev_name(dev));
|
||||
|
||||
else if (idtype == DEV_ID_TYPE_MPATH_UUID)
|
||||
_read_mpath_uuid(cmd, dev, &idname);
|
||||
|
||||
else if (idtype == DEV_ID_TYPE_LOOP_FILE)
|
||||
_read_loop_file(cmd, dev, &idname);
|
||||
|
||||
return idname;
|
||||
}
|
||||
|
||||
const char *idtype_to_str(uint16_t idtype)
|
||||
{
|
||||
if (idtype == DEV_ID_TYPE_SYS_WWID)
|
||||
return "sys_wwid";
|
||||
|
||||
if (idtype == DEV_ID_TYPE_SYS_SERIAL)
|
||||
return "sys_serial";
|
||||
|
||||
if (idtype == DEV_ID_TYPE_DEVNAME)
|
||||
return "devname";
|
||||
|
||||
if (idtype == DEV_ID_TYPE_MPATH_UUID)
|
||||
return "mpath_uuid";
|
||||
|
||||
if (idtype == DEV_ID_TYPE_LOOP_FILE)
|
||||
return "loop_file";
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
uint16_t idtype_from_str(const char *str)
|
||||
{
|
||||
if (!strcmp(str, "sys_wwid"))
|
||||
return DEV_ID_TYPE_SYS_WWID;
|
||||
if (!strcmp(str, "sys_serial"))
|
||||
return DEV_ID_TYPE_SYS_SERIAL;
|
||||
if (!strcmp(str, "devname"))
|
||||
return DEV_ID_TYPE_DEVNAME;
|
||||
if (!strcmp(str, "mpath_uuid"))
|
||||
return DEV_ID_TYPE_MPATH_UUID;
|
||||
if (!strcmp(str, "loop_file"))
|
||||
return DEV_ID_TYPE_LOOP_FILE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *dev_idtype(struct device *dev)
|
||||
{
|
||||
if (!dev->id)
|
||||
return NULL;
|
||||
|
||||
return idtype_to_str(dev->id->idtype);
|
||||
}
|
||||
|
||||
const char *dev_id(struct device *dev)
|
||||
{
|
||||
if (dev->id)
|
||||
return dev->id->idname;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _copy_idline_str(char *src, char *dst, int len)
|
||||
{
|
||||
char *s, *d = dst;
|
||||
|
||||
memset(dst, 0, len);
|
||||
|
||||
if (!(s = strchr(src, '=')))
|
||||
return;
|
||||
s++;
|
||||
while ((*s == ' ') && (s < src + len))
|
||||
s++;
|
||||
while ((*s != ' ') && (*s != '\0') && (*s != '\n') && (s < src + len)) {
|
||||
*d = *s;
|
||||
s++;
|
||||
d++;
|
||||
}
|
||||
}
|
||||
|
||||
int device_ids_read(struct cmd_context *cmd)
|
||||
{
|
||||
char line[PATH_MAX];
|
||||
char buf[PATH_MAX];
|
||||
char *idtype, *idname, *devname, *pvid;
|
||||
struct use_id *uid;
|
||||
FILE *fp;
|
||||
int fl_fd, fl_err = -1;
|
||||
int ret = 1;
|
||||
|
||||
/*
|
||||
* TODO: allow the use_device_ids list to come from a
|
||||
* command line option instead of devices_file?
|
||||
* If so, add use_id structs to use_device_ids based
|
||||
* on the reading the command line args here.
|
||||
*/
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return 1;
|
||||
|
||||
free_uids(&cmd->use_device_ids);
|
||||
|
||||
if (cmd->nolocking)
|
||||
goto use_file;
|
||||
if ((fl_fd = open(cmd->devices_file, O_RDWR)) < 0) {
|
||||
log_warn("Cannot open devices_file to flock.");
|
||||
goto use_file;
|
||||
}
|
||||
if ((fl_err = flock(fl_fd, LOCK_SH))) {
|
||||
log_warn("Cannot lock devices_file to read.");
|
||||
close(fl_fd);
|
||||
}
|
||||
|
||||
use_file:
|
||||
if (!(fp = fopen(cmd->devices_file, "r"))) {
|
||||
log_warn("Cannot open devices_file to read.");
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
idtype = strstr(line, "IDTYPE");
|
||||
idname = strstr(line, "IDNAME");
|
||||
devname = strstr(line, "DEVNAME");
|
||||
pvid = strstr(line, "PVID");
|
||||
|
||||
/* These two are the minimum required. */
|
||||
if (!idtype || !idname)
|
||||
continue;
|
||||
|
||||
if (!(uid = zalloc(sizeof(struct use_id))))
|
||||
return 0;
|
||||
|
||||
_copy_idline_str(idtype, buf, PATH_MAX);
|
||||
if (buf[0])
|
||||
uid->idtype = idtype_from_str(buf);
|
||||
|
||||
_copy_idline_str(idname, buf, PATH_MAX);
|
||||
if (buf[0])
|
||||
uid->idname = strdup(buf);
|
||||
|
||||
if (!uid->idtype || !uid->idname) {
|
||||
log_print("Ignoring device: %s", line);
|
||||
free_uid(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (devname) {
|
||||
_copy_idline_str(devname, buf, PATH_MAX);
|
||||
if (buf[0] && (buf[0] != '.'))
|
||||
uid->devname = strdup(buf);
|
||||
}
|
||||
|
||||
if (pvid) {
|
||||
_copy_idline_str(pvid, buf, PATH_MAX);
|
||||
if (buf[0] && (buf[0] != '.'))
|
||||
uid->pvid = strdup(buf);
|
||||
}
|
||||
|
||||
dm_list_add(&cmd->use_device_ids, &uid->list);
|
||||
}
|
||||
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
out:
|
||||
if (!cmd->nolocking && !fl_err) {
|
||||
if (flock(fl_fd, LOCK_UN))
|
||||
stack;
|
||||
if (close(fl_fd))
|
||||
stack;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int device_ids_write(struct cmd_context *cmd)
|
||||
{
|
||||
FILE *fp;
|
||||
time_t t;
|
||||
struct use_id *uid;
|
||||
const char *devname;
|
||||
const char *pvid;
|
||||
int fl_fd, fl_err = -1;
|
||||
int ret = 1;
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return 1;
|
||||
|
||||
if (cmd->nolocking)
|
||||
goto use_file;
|
||||
if ((fl_fd = open(cmd->devices_file, O_RDWR)) < 0) {
|
||||
log_warn("Cannot open devices_file to flock.");
|
||||
goto use_file;
|
||||
}
|
||||
if ((fl_err = flock(fl_fd, LOCK_EX))) {
|
||||
log_warn("Cannot lock devices_file to write.");
|
||||
close(fl_fd);
|
||||
}
|
||||
|
||||
use_file:
|
||||
if (!(fp = fopen(cmd->devices_file, "w"))) {
|
||||
log_warn("Cannot open devices_file to write.");
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
t = time(NULL);
|
||||
|
||||
log_print("updating devices_file");
|
||||
|
||||
fprintf(fp, "# LVM will use devices listed in this file.\n");
|
||||
fprintf(fp, "# IDTYPE and IDNAME fields are required, the DEVNAME path may change.\n");
|
||||
fprintf(fp, "# Created by LVM command %s pid %d at %s\n", cmd->name, getpid(), ctime(&t));
|
||||
|
||||
dm_list_iterate_items(uid, &cmd->use_device_ids) {
|
||||
devname = uid->dev ? dev_name(uid->dev) : ".";
|
||||
if (devname[0] != '/')
|
||||
devname = ".";
|
||||
|
||||
if (!uid->pvid || !uid->pvid[0] || (uid->pvid[0] == '.'))
|
||||
pvid = ".";
|
||||
else
|
||||
pvid = uid->pvid;
|
||||
|
||||
fprintf(fp, "IDTYPE=%s IDNAME=%s DEVNAME=%s PVID=%s\n",
|
||||
idtype_to_str(uid->idtype) ?: ".",
|
||||
uid->idname ?: ".", devname, pvid);
|
||||
}
|
||||
|
||||
if (fflush(fp))
|
||||
stack;
|
||||
if (fclose(fp))
|
||||
stack;
|
||||
|
||||
out:
|
||||
if (!cmd->nolocking && !fl_err) {
|
||||
if (flock(fl_fd, LOCK_UN))
|
||||
stack;
|
||||
if (close(fl_fd))
|
||||
stack;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct use_id *_get_uid_for_dev(struct cmd_context *cmd, struct device *dev)
|
||||
{
|
||||
struct use_id *uid;
|
||||
|
||||
dm_list_iterate_items(uid, &cmd->use_device_ids) {
|
||||
if (uid->dev == dev)
|
||||
return uid;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add or update entry for this dev.
|
||||
* IDTYPE=sys_wwid IDNAME=01234566 DEVNAME=/dev/sdb PVID=99393939 [OPTS=xx,yy,zz]
|
||||
*
|
||||
* add an entry to dev->ids and point dev->id to it.
|
||||
* add or update entry in cmd->use_device_ids
|
||||
*/
|
||||
int device_id_pvcreate(struct cmd_context *cmd, struct device *dev, const char *pvid,
|
||||
const char *idtype_arg, const char *id_arg)
|
||||
{
|
||||
uint16_t idtype = 0;
|
||||
char *idname;
|
||||
struct use_id *uid;
|
||||
struct dev_id *did;
|
||||
int found_did = 0;
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return 1;
|
||||
|
||||
/* TODO: handle dev with existing match */
|
||||
if (dev->id || (dev->flags & DEV_MATCHED_USE_ID))
|
||||
return 0;
|
||||
|
||||
if (!(uid = _get_uid_for_dev(cmd, dev))) {
|
||||
if (!(uid = zalloc(sizeof(struct use_id))))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* TODO: should deviceidtype command line option work for mpath/loop? */
|
||||
/* TODO: add more idtypes for special devs (e.g. MD) that don't have wwid */
|
||||
|
||||
if (_dev_has_mpath_uuid(cmd, dev, &idname)) {
|
||||
idtype = DEV_ID_TYPE_MPATH_UUID;
|
||||
goto id_done;
|
||||
}
|
||||
|
||||
if (MAJOR(dev->dev) == cmd->dev_types->loop_major) {
|
||||
idtype = DEV_ID_TYPE_LOOP_FILE;
|
||||
goto id_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* First use type specified by user option, then use a previous
|
||||
* type, then use the default type.
|
||||
* TODO: allow lvm.conf device_id_types to control idtypes used here?
|
||||
*/
|
||||
|
||||
if (idtype_arg) {
|
||||
if (!(idtype = idtype_from_str(idtype_arg)))
|
||||
log_warn("WARNING: ignoring unknown device_id type %s.", idtype_arg);
|
||||
else {
|
||||
if (id_arg) {
|
||||
idname = id_arg;
|
||||
goto id_done;
|
||||
}
|
||||
goto id_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!idtype && uid->idtype) {
|
||||
idtype = uid->idtype;
|
||||
log_print("Create device_id for %s using previous idtype %u", dev_name(dev), idtype);
|
||||
goto id_name;
|
||||
}
|
||||
|
||||
idtype = DEV_ID_TYPE_SYS_WWID;
|
||||
|
||||
id_name:
|
||||
if (!(idname = _device_id_system_read(cmd, dev, idtype))) {
|
||||
if (idtype == DEV_ID_TYPE_SYS_WWID) {
|
||||
idtype = DEV_ID_TYPE_SYS_SERIAL;
|
||||
goto id_name;
|
||||
}
|
||||
idtype = DEV_ID_TYPE_DEVNAME;
|
||||
goto id_name;
|
||||
}
|
||||
|
||||
id_done:
|
||||
dm_list_iterate_items(did, &dev->ids) {
|
||||
if (did->idtype == idtype) {
|
||||
/* TODO: verify did->idname matches idname */
|
||||
found_did = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_did) {
|
||||
if (!(did = zalloc(sizeof(struct dev_id))))
|
||||
return_0;
|
||||
did->idtype = idtype;
|
||||
did->idname = idname;
|
||||
did->dev = dev;
|
||||
dm_list_add(&dev->ids, &did->list);
|
||||
}
|
||||
dev->id = did;
|
||||
dev->flags |= DEV_MATCHED_USE_ID;
|
||||
|
||||
if (uid->idtype && (uid->idtype != idtype)) {
|
||||
log_print("Changing device_id_type from %s to %s for %s",
|
||||
idtype_to_str(uid->idtype), idtype_to_str(idtype), dev_name(dev));
|
||||
}
|
||||
if (uid->idtype && (uid->idtype == idtype) &&
|
||||
strcmp(uid->idname, idname)) {
|
||||
log_print("Changing device_id from %s to %s for %s",
|
||||
uid->idname, idname, dev_name(dev));
|
||||
}
|
||||
|
||||
uid->idtype = did->idtype;
|
||||
uid->idname = strdup(did->idname);
|
||||
uid->devname = strdup(dev_name(dev));
|
||||
uid->dev = dev;
|
||||
uid->pvid = strdup(pvid);
|
||||
|
||||
if (!uid->idname || !uid->idname || !uid->pvid) {
|
||||
free_uid(uid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dm_list_add(&cmd->use_device_ids, &uid->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update entry for this dev.
|
||||
* Set PVID=.
|
||||
* update entry in cmd->use_device_ids
|
||||
*/
|
||||
void device_id_pvremove(struct cmd_context *cmd, struct device *dev)
|
||||
{
|
||||
struct use_id *uid;
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return;
|
||||
|
||||
if (!(uid = _get_uid_for_dev(cmd, dev))) {
|
||||
log_warn("WARNING: use_device_ids does not include %s", dev_name(dev));
|
||||
return;
|
||||
}
|
||||
|
||||
if (uid->pvid) {
|
||||
free(uid->pvid);
|
||||
uid->pvid = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* check for dev->ids entry with uid->idtype, if found compare it,
|
||||
* if not, system_read of this type and add entry to dev->ids, compare it.
|
||||
* When a match is found, set up links among uid/did/dev.
|
||||
*/
|
||||
|
||||
int device_id_match(struct cmd_context *cmd, struct use_id *uid, struct device *dev)
|
||||
{
|
||||
struct dev_id *did;
|
||||
char *idname;
|
||||
|
||||
dm_list_iterate_items(did, &dev->ids) {
|
||||
if (did->idtype == uid->idtype) {
|
||||
if (did->idname && !strcmp(did->idname, uid->idname)) {
|
||||
uid->dev = dev;
|
||||
dev->id = did;
|
||||
dev->flags |= DEV_MATCHED_USE_ID;
|
||||
return 1;
|
||||
} else {
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(did = zalloc(sizeof(struct dev_id))))
|
||||
return_0;
|
||||
|
||||
if (!(idname = _device_id_system_read(cmd, dev, uid->idtype))) {
|
||||
/* Save a new did in dev->ids for this type to indicate no match
|
||||
to avoid repeated system_read, since this called many times.
|
||||
Setting idtype and NULL idname means no id of this type. */
|
||||
did->idtype = uid->idtype;
|
||||
did->dev = dev;
|
||||
dm_list_add(&dev->ids, &did->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Save this id for the device (so it can be quickly checked again), even
|
||||
if it's not the idtype used to identify the dev in device_id_file. */
|
||||
did->idtype = uid->idtype;
|
||||
did->idname = idname;
|
||||
did->dev = dev;
|
||||
dm_list_add(&dev->ids, &did->list);
|
||||
|
||||
if (!strcmp(idname, uid->idname)) {
|
||||
uid->dev = dev;
|
||||
dev->id = did;
|
||||
dev->flags |= DEV_MATCHED_USE_ID;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
pvid is needed in the devices_file, and wwid (device_id more generally)
|
||||
is needed in metadata in order to handle cases where a device has no wwid
|
||||
or the wwid changes. In these cases the correct set of devices can be
|
||||
found and the devices_file can be corrected. (A wwid in the metadata will
|
||||
also eliminate the problem of duplicate pvs for those devices.)
|
||||
|
||||
Three identifiers: wwid, devname, pvid
|
||||
- devname can change, cannot be duplicated, cannot be unknown
|
||||
- wwid can change (rare), can be duplicated (rare), can be unknown
|
||||
- pvid cannot change, can be duplicated, cannot be unknown
|
||||
|
||||
(wwid is more generally the device_id, and would only change or
|
||||
be duplicated when the device_id is not a wwid but some other
|
||||
identifier used when wwid is not available.)
|
||||
|
||||
|
||||
if devname changes
|
||||
------------------
|
||||
. if wwid exists, lvm corrects devname (by reading wwid of all devices)
|
||||
. if no wwid exists for the entry in devices_file, and new devname is
|
||||
out of the devices_file, then PV appears missing. lvm would need
|
||||
to read headers from all devices on the system to find the PVID, or
|
||||
the user could run "lvmdevices --addpvid <pvid>" to read device headers
|
||||
to find PVID. When found, devices_file is updated.
|
||||
. if no wwid, and the new devname is in devices_file, then lvm will
|
||||
find the PV on the new devname during normal scan, and update the
|
||||
devices_file.
|
||||
|
||||
|
||||
if wwid changes
|
||||
---------------
|
||||
. same underlying storage, different wwid reported for it
|
||||
|
||||
. if the new wwid is not used in another devices_file entry
|
||||
devices_file: WWID=XXX DEVNAME=/dev/foo PVID=AAA
|
||||
after reboot the wwid for the device changes to YYY
|
||||
YYY does not appear in devices_file or lvm metadata, so lvm doesn't know to scan
|
||||
the dev with that wwid
|
||||
device_ids_match() will find no dev with XXX, so uid->dev will be null
|
||||
the PV appears to be missing
|
||||
user runs a cmd to scan all devnames on the system (outside devices_file)
|
||||
to find a device with pvid AAA. when it's found, the cmd updates devices_file
|
||||
entry to have WWID=YYY PVID=AAA. "lvmdevices --addpvid AAA"
|
||||
(if the devname for this entry remained unchanged, then lvm could likely
|
||||
avoid scanning all devs, by just scanning /dev/foo and finding AAA,
|
||||
this is basically an optimization that could be applied automatically
|
||||
and might avoid requiring the user to run a cmd to find the pv)
|
||||
|
||||
. new wwid value is included in devices_file for a different PV,
|
||||
causing duplicate wwids
|
||||
devices_file: WWID=XXX DEVNAME=/dev/foo PVID=AAA
|
||||
WWID=YYY DEVNAME=/dev/bar PVID=BBB
|
||||
after reboot the wwid for the first device changes to YYY
|
||||
device_ids_match() will see two devs with wwid YYY
|
||||
lvm will scan both devices and find one with AAA and the other BBB
|
||||
lvm will update devices_file to have WWID=YYY for both devs
|
||||
lvm may suggest using a different idtype if that would help
|
||||
|
||||
. two wwids in devices_file are swapped
|
||||
devices_file: WWID=XXX DEVNAME=/dev/foo PVID=AAA
|
||||
WWID=YYY DEVNAME=/dev/bar PVID=BBB
|
||||
the wwid for AAA changes to YYY
|
||||
the wwid for BBB changes to XXX
|
||||
device_ids_match() will seem to be ok (possibly complain about devnames)
|
||||
both devs will be scanned by label_scan
|
||||
device_ids_validate() will see different pvids for each entry
|
||||
and will update devices_file
|
||||
|
||||
|
||||
if pvid and wwid are both duplicated
|
||||
------------------------------------
|
||||
. devices_file: WWID=XXX DEVNAME=/dev/foo PVID=AAA
|
||||
new state:
|
||||
WWID=XXX DEVNAME=/dev/foo PVID=AAA
|
||||
WWID=XXX DEVNAME=/dev/bar PVID=AAA
|
||||
This would fall back to the old duplicate device handling.
|
||||
We would need to keep the old duplicate pv handling to handle
|
||||
this case.
|
||||
If the wwid originates from data blocks on the storage,
|
||||
then this can easily happen by cloning the disks.
|
||||
|
||||
|
||||
if wwids begin as duplicates
|
||||
----------------------------
|
||||
. lvm can still use the wwid for filtering,
|
||||
but it won't help if pvid is also duplicated
|
||||
|
||||
|
||||
if pvid is duplicated but wwid is not
|
||||
-------------------------------------
|
||||
. lvm will use the wwid to choose the right one
|
||||
|
||||
|
||||
if wwid is unknown
|
||||
------------------
|
||||
. and no other unique device_id is available
|
||||
. this would work similarly to the old filter accepting only a list of devnames
|
||||
. if devname changes and new devname in list, lvm fixes devname
|
||||
. if devname changes and new devname out of filter, pv missing, then
|
||||
either automatically read all devs to find pvid, or have user run cmd to do this
|
||||
(lvmdevices --addpvid <pvid> would read every device on the system for header with pvid,
|
||||
and if found update devices_file with the new devname/pvid)
|
||||
*/
|
||||
|
||||
/*
|
||||
* For each entry on cmd->use_device_ids, find a struct device from dev-cache.
|
||||
* This must not open or read devices. filters are applied after this,
|
||||
* and they may open devs in the first filter stage. The second filtering
|
||||
* stage, done as a part of label_scan, is finally allowed to read devices.
|
||||
*
|
||||
* When a device id of a particular type is read for a dev, a did for that
|
||||
* type is saved in dev->ids in case it needs to be checked again.
|
||||
*
|
||||
* When a particular dev_id for a dev (in dev-cache) is matched to a use_dev
|
||||
* (from use_device_ids), then:
|
||||
* . uid->dev = dev;
|
||||
* . dev->id = did;
|
||||
* . dev->flags |= DEV_MATCHED_USE_ID;
|
||||
*/
|
||||
|
||||
void device_ids_match(struct cmd_context *cmd)
|
||||
{
|
||||
struct dev_iter *iter;
|
||||
struct use_id *uid;
|
||||
struct device *dev;
|
||||
int update_file = 0;
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return;
|
||||
|
||||
dm_list_iterate_items(uid, &cmd->use_device_ids) {
|
||||
/* already matched */
|
||||
if (uid->dev && (uid->dev->flags & DEV_MATCHED_USE_ID))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* uid->devname may be incorrect, but it's often correct, so it's the
|
||||
* most efficient place to begin.
|
||||
*/
|
||||
if (uid->devname &&
|
||||
(dev = dev_cache_get(cmd, uid->devname, NULL))) {
|
||||
/* On success, device_id_match() links the uid, dev, and did. */
|
||||
if (device_id_match(cmd, uid, dev))
|
||||
continue;
|
||||
else {
|
||||
/* uid->devname now belongs to a different device */
|
||||
log_print("Device with name %s has changed.", uid->devname);
|
||||
}
|
||||
}
|
||||
|
||||
/* At a minimum some devname needs to be added or updated.
|
||||
device_ids_validate may find other reasons to update the
|
||||
file. Are there commands where device_ids_validate would
|
||||
not be run, so we should update the file here? */
|
||||
|
||||
/*
|
||||
* Iterate through all devs and try to match uid.
|
||||
*
|
||||
* A filter is not used when iterating since this is not selecting
|
||||
* devs to use. The next dev iteration in label scan does use filters.
|
||||
* The device_id matches created here are used by filter-deviceid later.
|
||||
* (Might we apply a couple simple filters here, though, to avoid
|
||||
* doing some pointless match attempts?)
|
||||
*
|
||||
* If a match is made here it means the uid->devname is wrong so the
|
||||
* device_id file should be udpated with a new devname.
|
||||
*/
|
||||
if (!(iter = dev_iter_create(NULL, 0)))
|
||||
continue;
|
||||
while ((dev = dev_iter_get(cmd, iter))) {
|
||||
if (dev->flags & DEV_MATCHED_USE_ID)
|
||||
continue;
|
||||
if (device_id_match(cmd, uid, dev))
|
||||
break;
|
||||
}
|
||||
dev_iter_destroy(iter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for entries in devices_file for which we found no device.
|
||||
*/
|
||||
dm_list_iterate_items(uid, &cmd->use_device_ids) {
|
||||
if (uid->dev && (uid->dev->flags & DEV_MATCHED_USE_ID))
|
||||
continue;
|
||||
|
||||
if (uid->dev && !(uid->dev->flags & DEV_MATCHED_USE_ID)) {
|
||||
/* is this possible? */
|
||||
}
|
||||
|
||||
log_print("Device with previous name %s not found with %s %s PVID %s.",
|
||||
uid->devname, idtype_to_str(uid->idtype), uid->idname, uid->pvid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called after label_scan() to compare what was found on disks
|
||||
* vs what's in the devices_file. The devices_file could be outdated
|
||||
* and need correcting; the authoritative data is what's on disk.
|
||||
* Now that we have read the device labels in label_scan and have the PVID's
|
||||
* we can check the pvid's of use_device_ids entries from the device_id_file.
|
||||
*/
|
||||
void device_ids_validate(struct cmd_context *cmd)
|
||||
{
|
||||
struct use_id *uid;
|
||||
int update_file = 0;
|
||||
|
||||
if (!cmd->enable_device_ids)
|
||||
return;
|
||||
|
||||
dm_list_iterate_items(uid, &cmd->use_device_ids) {
|
||||
if (!uid->dev)
|
||||
continue;
|
||||
|
||||
if (uid->dev->pvid[0] && (strcmp(uid->dev->pvid, uid->pvid))) {
|
||||
log_print("Device %s has updated PVID %s from devices_file (%s)",
|
||||
dev_name(uid->dev), uid->dev->pvid, uid->pvid);
|
||||
if (uid->pvid)
|
||||
free(uid->pvid);
|
||||
uid->pvid = strdup(uid->dev->pvid);
|
||||
update_file = 1;
|
||||
}
|
||||
|
||||
if (!uid->devname || strcmp(dev_name(uid->dev), uid->devname)) {
|
||||
log_print("Device %s has updated devname from devices_file (%s).",
|
||||
dev_name(uid->dev), uid->devname ?: ".");
|
||||
if (uid->devname)
|
||||
free(uid->devname);
|
||||
uid->devname = strdup(dev_name(uid->dev));
|
||||
update_file = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_file)
|
||||
device_ids_write(cmd);
|
||||
|
||||
/*
|
||||
* Issue: if devices have no wwid or serial numbers, entries in
|
||||
* devices_file are identified only by their unstable devnames.
|
||||
* It the devnames then change, all devices on the system need to
|
||||
* read to find the PVs. Reading all devices on the system is
|
||||
* one thing that the devices_file is meant to avoid. A new
|
||||
* config setting could be used to enable/disable this behavior.
|
||||
*
|
||||
* TODO: if there are entries on use_device_ids that have no dev
|
||||
* and are using DEV_ID_TYPE_DEVNAME, then it's possible that the
|
||||
* unstable devname simply changed and the new devname was not
|
||||
* included in devices_file. We need to read all devices on the
|
||||
* system to find one with the missing PVID, label_scan it, and
|
||||
* update devices_file with the new devname for the PVID.
|
||||
*
|
||||
* This function should tell setup_devices() that it should do a
|
||||
* label_scan_for_pvid() (which only reads the headers for a PVID)
|
||||
* on system devices that it did not already cover in the completed
|
||||
* label_scan(). label_scan_for_pvid() would get a list of pvids
|
||||
* to look for and return a list of devs on which they are found.
|
||||
* That list of devs would then be passed to label_scan_devs()
|
||||
* to do the full label_scan on them.
|
||||
*
|
||||
* A config setting would be able to disable this automatic scan
|
||||
* of all devs for missing pvids, and the usage of the new devnames
|
||||
* where those PVs are found. Without this scan, PVs on unstable
|
||||
* devnames would be missing until a user manually runs a command
|
||||
* to search devices for the missing PVs. The user could selectively
|
||||
* scan certain devs and avoid devs that should not be touched.
|
||||
*/
|
||||
}
|
||||
|
||||
int devices_file_valid(struct cmd_context *cmd)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
if (!cmd->devices_file || !strlen(cmd->devices_file))
|
||||
return 0;
|
||||
|
||||
if (stat(cmd->devices_file, &buf))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
38
lib/device/device_id.h
Normal file
38
lib/device/device_id.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _LVM_DEVICE_ID_H
|
||||
#define _LVM_DEVICE_ID_H
|
||||
|
||||
void free_uid(struct use_id *uid);
|
||||
void free_uids(struct dm_list *list);
|
||||
void free_did(struct dev_id *did);
|
||||
void free_dids(struct dm_list *list);
|
||||
const char *idtype_to_str(uint16_t idtype);
|
||||
uint16_t idtype_from_str(const char *str);
|
||||
const char *dev_idtype(struct device *dev);
|
||||
const char *dev_id(struct device *dev);
|
||||
int device_ids_read(struct cmd_context *cmd);
|
||||
int device_ids_write(struct cmd_context *cmd);
|
||||
int device_id_pvcreate(struct cmd_context *cmd, struct device *dev, const char *pvid,
|
||||
const char *idtype_arg, const char *id_arg);
|
||||
void device_id_pvremove(struct cmd_context *cmd, struct device *dev);
|
||||
int device_id_match(struct cmd_context *cmd, struct use_id *uid, struct device *dev);
|
||||
void device_ids_match(struct cmd_context *cmd);
|
||||
void device_ids_validate(struct cmd_context *cmd);
|
||||
|
||||
int devices_file_valid(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
56
lib/filters/filter-deviceid.c
Normal file
56
lib/filters/filter-deviceid.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "base/memory/zalloc.h"
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/filters/filter.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
|
||||
static int _passes_deviceid_filter(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
|
||||
{
|
||||
if (!cmd->enable_device_ids)
|
||||
return 1;
|
||||
|
||||
if (dev->flags & DEV_MATCHED_USE_ID)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _destroy_deviceid_filter(struct dev_filter *f)
|
||||
{
|
||||
if (f->use_count)
|
||||
log_error(INTERNAL_ERROR "Destroying deviceid filter while in use %u times.", f->use_count);
|
||||
|
||||
free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *deviceid_filter_create(struct cmd_context *cmd)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = zalloc(sizeof(struct dev_filter)))) {
|
||||
log_error("deviceid filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = _passes_deviceid_filter;
|
||||
f->destroy = _destroy_deviceid_filter;
|
||||
f->use_count = 0;
|
||||
f->name = "deviceid";
|
||||
|
||||
log_debug_devs("deviceid filter initialised.");
|
||||
|
||||
return f;
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "lib/misc/lib.h"
|
||||
#include "lib/filters/filter.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
|
||||
struct rfilter {
|
||||
struct dm_pool *mem;
|
||||
@@ -151,6 +152,11 @@ static int _accept_p(struct cmd_context *cmd, struct dev_filter *f, struct devic
|
||||
struct rfilter *rf = (struct rfilter *) f->private;
|
||||
struct dm_str_list *sl;
|
||||
|
||||
if (cmd->enable_device_ids) {
|
||||
/* TODO: print a notice if the filter is set to something and we ignore it here. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(sl, &dev->aliases) {
|
||||
m = dm_regex_match(rf->engine, sl->str);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ struct dev_filter *partitioned_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *persistent_filter_create(struct dev_types *dt, struct dev_filter *f);
|
||||
struct dev_filter *sysfs_filter_create(void);
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *deviceid_filter_create(struct cmd_context *cmd);
|
||||
|
||||
struct dev_filter *internal_filter_create(void);
|
||||
int internal_filter_allow(struct dm_pool *mem, struct device *dev);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "lib/metadata/segtype.h"
|
||||
#include "lib/format_text/text_export.h"
|
||||
#include "lib/commands/toolcontext.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "libdaemon/client/config-util.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
@@ -555,6 +556,11 @@ static int _print_pvs(struct formatter *f, struct volume_group *vg)
|
||||
dm_escape_double_quotes(buffer, pv_dev_name(pv)));
|
||||
outnl(f);
|
||||
|
||||
if (dev_idtype(pv->dev) && dev_id(pv->dev)) {
|
||||
outf(f, "device_id_type = \"%s\"", dev_idtype(pv->dev));
|
||||
outf(f, "device_id = \"%s\"", dev_id(pv->dev));
|
||||
}
|
||||
|
||||
if (!_print_flag_config(f, pv->status, PV_FLAGS))
|
||||
return_0;
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ static int _read_pv(struct cmd_context *cmd,
|
||||
struct physical_volume *pv;
|
||||
struct pv_list *pvl;
|
||||
const struct dm_config_value *cv;
|
||||
const char *device_hint;
|
||||
const char *str;
|
||||
uint64_t size, ba_start;
|
||||
|
||||
if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
|
||||
@@ -233,11 +233,21 @@ static int _read_pv(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_config_get_str(pvn, "device", &device_hint)) {
|
||||
if (!(pv->device_hint = dm_pool_strdup(mem, device_hint)))
|
||||
if (dm_config_get_str(pvn, "device", &str)) {
|
||||
if (!(pv->device_hint = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device hint in read_pv.");
|
||||
}
|
||||
|
||||
if (dm_config_get_str(pvn, "device_id", &str)) {
|
||||
if (!(pv->device_id = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device_id in read_pv.");
|
||||
}
|
||||
|
||||
if (dm_config_get_str(pvn, "device_id_type", &str)) {
|
||||
if (!(pv->device_id_type = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device_id_type in read_pv.");
|
||||
}
|
||||
|
||||
if (!_read_uint64(pvn, "pe_start", &pv->pe_start)) {
|
||||
log_error("Couldn't read extent start value (pe_start) "
|
||||
"for physical volume.");
|
||||
@@ -306,7 +316,7 @@ static int _read_pvsummary(struct cmd_context *cmd,
|
||||
{
|
||||
struct physical_volume *pv;
|
||||
struct pv_list *pvl;
|
||||
const char *device_hint;
|
||||
const char *str;
|
||||
|
||||
if (!(pvl = dm_pool_zalloc(mem, sizeof(*pvl))) ||
|
||||
!(pvl->pv = dm_pool_zalloc(mem, sizeof(*pvl->pv))))
|
||||
@@ -326,9 +336,19 @@ static int _read_pvsummary(struct cmd_context *cmd,
|
||||
!_read_uint64(pvn, "dev_size", &pv->size))
|
||||
log_warn("Couldn't read dev size for physical volume.");
|
||||
|
||||
if (dm_config_get_str(pvn, "device", &device_hint)) {
|
||||
if (!(pv->device_hint = dm_pool_strdup(mem, device_hint)))
|
||||
log_error("Failed to allocate memory for device hint in read_pv.");
|
||||
if (dm_config_get_str(pvn, "device", &str)) {
|
||||
if (!(pv->device_hint = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device hint in read_pv_sum.");
|
||||
}
|
||||
|
||||
if (dm_config_get_str(pvn, "device_id", &str)) {
|
||||
if (!(pv->device_id = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device_id in read_pv_sum.");
|
||||
}
|
||||
|
||||
if (dm_config_get_str(pvn, "device_id_type", &str)) {
|
||||
if (!(pv->device_id_type = dm_pool_strdup(mem, str)))
|
||||
log_error("Failed to allocate memory for device_id_type in read_pv_sum.");
|
||||
}
|
||||
|
||||
dm_list_add(&vgsummary->pvsummaries, &pvl->list);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "lib/label/hints.h"
|
||||
#include "lib/metadata/metadata.h"
|
||||
#include "lib/format_text/layout.h"
|
||||
#include "lib/device/device_id.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -684,7 +685,8 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
if (!_scan_dev_open(devl->dev)) {
|
||||
log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
|
||||
dm_list_del(&devl->list);
|
||||
dm_list_add(&reopen_devs, &devl->list);
|
||||
lvmcache_del_dev(devl->dev);
|
||||
scan_failed_count++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -779,16 +781,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This will search the system's /dev for new path names and
|
||||
* could help us reopen the device if it finds a new preferred
|
||||
* path name for this dev's major:minor. It does that by
|
||||
* inserting a new preferred path name on dev->aliases. open
|
||||
* uses the first name from that list.
|
||||
*/
|
||||
log_debug_devs("Scanning refreshing device paths.");
|
||||
dev_cache_scan();
|
||||
|
||||
/* Put devs that failed to open back on the original list to retry. */
|
||||
dm_list_splice(devs, &reopen_devs);
|
||||
goto scan_more;
|
||||
@@ -922,7 +914,17 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev
|
||||
|
||||
dm_list_init(&devs);
|
||||
|
||||
dev_cache_scan();
|
||||
/*
|
||||
* Creates a list of available devices, does not open or read any,
|
||||
* and does not filter them.
|
||||
*/
|
||||
setup_devices(cmd);
|
||||
|
||||
/*
|
||||
* Iterating over all available devices with cmd->filter filters
|
||||
* devices; those returned from dev_iter_get are the devs that
|
||||
* pass filters, and are those we can use.
|
||||
*/
|
||||
|
||||
if (!(iter = dev_iter_create(cmd->filter, 0))) {
|
||||
log_error("Scanning failed to get devices.");
|
||||
@@ -1005,12 +1007,14 @@ int label_scan(struct cmd_context *cmd)
|
||||
dm_list_init(&hints_list);
|
||||
|
||||
/*
|
||||
* dev_cache_scan() creates a list of devices on the system
|
||||
* (saved in in dev-cache) which we can iterate through to
|
||||
* search for LVM devs. The dev cache list either comes from
|
||||
* looking at dev nodes under /dev, or from udev.
|
||||
* Creates a list of available devices, does not open or read any,
|
||||
* and does not filter them. The list of all available devices
|
||||
* is kept in "dev-cache", and comes from /dev entries or libudev.
|
||||
* The list of devs found here needs to be filtered to get the
|
||||
* list of devs we can use. The dev_iter calls using cmd->filter
|
||||
* are what filters the devs.
|
||||
*/
|
||||
dev_cache_scan();
|
||||
setup_devices(cmd);
|
||||
|
||||
/*
|
||||
* If we know that there will be md components with an end
|
||||
@@ -1212,6 +1216,8 @@ int label_scan(struct cmd_context *cmd)
|
||||
if (create_hints)
|
||||
write_hint_file(cmd, create_hints);
|
||||
|
||||
device_ids_validate(cmd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -4875,8 +4875,10 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
|
||||
}
|
||||
}
|
||||
|
||||
if (found_old_metadata)
|
||||
if (found_old_metadata) {
|
||||
log_warn("WARNING: Inconsistent metadata found for VG %s.", vgname);
|
||||
log_warn("See vgck --updatemetadata to correct inconsistency.");
|
||||
}
|
||||
|
||||
vg = NULL;
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ struct physical_volume {
|
||||
struct id old_id; /* Set during pvchange -u. */
|
||||
struct device *dev;
|
||||
const char *device_hint; /* primary name last time metadata was written */
|
||||
const char *device_id;
|
||||
const char *device_id_type;
|
||||
const struct format_type *fmt;
|
||||
struct format_instance *fid;
|
||||
|
||||
|
||||
@@ -162,6 +162,18 @@ lvconvert - Change logical volume layout
|
||||
\fB-q\fP|\fB--quiet\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegrity\fP \fBy\fP|\fBn\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegrityblocksize\fP \fINumber\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegritymode\fP \fIString\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB-r\fP|\fB--readahead\fP \fBauto\fP|\fBnone\fP|\fINumber\fP
|
||||
.ad b
|
||||
@@ -982,6 +994,28 @@ Poll LV to continue conversion.
|
||||
.br
|
||||
-
|
||||
|
||||
Add or remove data integrity checksums to raid images.
|
||||
.br
|
||||
.P
|
||||
\fBlvconvert\fP \fB--raidintegrity\fP \fBy\fP|\fBn\fP \fILV\fP\fI_raid\fP
|
||||
.br
|
||||
.RS 4
|
||||
.ad l
|
||||
[ \fB--raidintegritymode\fP \fIString\fP ]
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
[ \fB--raidintegrityblocksize\fP \fINumber\fP ]
|
||||
.ad b
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.RE
|
||||
.br
|
||||
.RS 4
|
||||
[ \fIPV\fP ... ]
|
||||
.RE
|
||||
-
|
||||
|
||||
Common options for command:
|
||||
.
|
||||
.RS 4
|
||||
@@ -1405,6 +1439,35 @@ Repeat once to also suppress any prompts with answer 'no'.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegrity\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
Enable or disable data integrity checksums for raid images.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegrityblocksize\fP \fINumber\fP
|
||||
.br
|
||||
The block size to use for dm-integrity on raid images.
|
||||
The integrity block size should usually match the device
|
||||
logical block size, or the file system block size.
|
||||
It may be less than the file system block size, but not
|
||||
less than the device logical block size.
|
||||
Possible values: 512, 1024, 2048, 4096.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegritymode\fP \fIString\fP
|
||||
.br
|
||||
Use a journal (default) or bitmap for keeping integrity checksums consistent
|
||||
in case of a crash. The bitmap areas are recalculated after a crash, so corruption
|
||||
in those areas would not be detected. A journal does not have this problem.
|
||||
The journal mode doubles writes to storage, but can improve performance for
|
||||
scattered writes packed into a single journal write.
|
||||
bitmap mode can in theory achieve full write throughput of the device,
|
||||
but would not benefit from the potential scattered write optimization.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB-r\fP|\fB--readahead\fP \fBauto\fP|\fBnone\fP|\fINumber\fP
|
||||
.br
|
||||
Sets read ahead sector count of an LV.
|
||||
|
||||
@@ -186,6 +186,18 @@ lvcreate - Create a logical volume
|
||||
\fB-q\fP|\fB--quiet\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegrity\fP \fBy\fP|\fBn\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegrityblocksize\fP \fINumber\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB--raidintegritymode\fP \fIString\fP
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
\fB-r\fP|\fB--readahead\fP \fBauto\fP|\fBnone\fP|\fINumber\fP
|
||||
.ad b
|
||||
@@ -425,6 +437,18 @@ Create a raid LV (a specific raid level must be used, e.g. raid1).
|
||||
[ \fB--[raid]maxrecoveryrate\fP \fISize\fP[k|UNIT] ]
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
[ \fB--raidintegrity\fP \fBy\fP|\fBn\fP ]
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
[ \fB--raidintegritymode\fP \fIString\fP ]
|
||||
.ad b
|
||||
.br
|
||||
.ad l
|
||||
[ \fB--raidintegrityblocksize\fP \fINumber\fP ]
|
||||
.ad b
|
||||
.br
|
||||
[ COMMON_OPTIONS ]
|
||||
.RE
|
||||
.br
|
||||
@@ -1420,6 +1444,35 @@ Repeat once to also suppress any prompts with answer 'no'.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegrity\fP \fBy\fP|\fBn\fP
|
||||
.br
|
||||
Enable or disable data integrity checksums for raid images.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegrityblocksize\fP \fINumber\fP
|
||||
.br
|
||||
The block size to use for dm-integrity on raid images.
|
||||
The integrity block size should usually match the device
|
||||
logical block size, or the file system block size.
|
||||
It may be less than the file system block size, but not
|
||||
less than the device logical block size.
|
||||
Possible values: 512, 1024, 2048, 4096.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB--raidintegritymode\fP \fIString\fP
|
||||
.br
|
||||
Use a journal (default) or bitmap for keeping integrity checksums consistent
|
||||
in case of a crash. The bitmap areas are recalculated after a crash, so corruption
|
||||
in those areas would not be detected. A journal does not have this problem.
|
||||
The journal mode doubles writes to storage, but can improve performance for
|
||||
scattered writes packed into a single journal write.
|
||||
bitmap mode can in theory achieve full write throughput of the device,
|
||||
but would not benefit from the potential scattered write optimization.
|
||||
.ad b
|
||||
.HP
|
||||
.ad l
|
||||
\fB-r\fP|\fB--readahead\fP \fBauto\fP|\fBnone\fP|\fINumber\fP
|
||||
.br
|
||||
Sets read ahead sector count of an LV.
|
||||
|
||||
@@ -112,7 +112,7 @@ version without the system ID feature.
|
||||
|
||||
A local VG is meant to be used by a single host.
|
||||
|
||||
A shared VG is meant to be used by multiple hosts.
|
||||
A shared or clustered VG is meant to be used by multiple hosts.
|
||||
|
||||
These can be further distinguished as:
|
||||
|
||||
@@ -168,31 +168,16 @@ global {
|
||||
}
|
||||
.fi
|
||||
|
||||
.TP
|
||||
.B appmachineid
|
||||
.br
|
||||
|
||||
An LVM-specific derivation of /etc/machine-id is used as the system ID.
|
||||
See
|
||||
.BR machine-id (5)
|
||||
to check if machine-id is available on the host.
|
||||
|
||||
.I lvm.conf
|
||||
.nf
|
||||
global {
|
||||
system_id_source = "appmachineid"
|
||||
}
|
||||
.fi
|
||||
|
||||
.TP
|
||||
.B machineid
|
||||
.br
|
||||
|
||||
The content of /etc/machine-id is used as the system ID.
|
||||
The content of /etc/machine-id is used as the system ID if available.
|
||||
See
|
||||
.BR machine-id (5)
|
||||
and
|
||||
.BR systemd-machine-id-setup (1)
|
||||
to check if machine-id is available on the host.
|
||||
(appmachineid is recommended in place of machineid.)
|
||||
|
||||
.I lvm.conf
|
||||
.nf
|
||||
|
||||
@@ -127,4 +127,17 @@ umount "$mount_dir"
|
||||
lvchange -an $vg/$lv1
|
||||
lvchange -an $vg/$lv2
|
||||
|
||||
# misc tests
|
||||
|
||||
lvremove $vg
|
||||
|
||||
lvcreate -n $lv1 -l 2 -an $vg "$dev1"
|
||||
lvcreate -n $lv2 -l 2 -an $vg "$dev1"
|
||||
lvcreate -n $lv3 -l 2 -an $vg "$dev2"
|
||||
|
||||
lvconvert -y --type writecache --cachevol $lv3 $vg/$lv1
|
||||
not lvconvert -y --type writecache --cachevol ${lv3}_cvol $vg/$lv2
|
||||
not lvconvert -y --type cache --cachevol ${lv3}_cvol $vg/$lv2
|
||||
not lvconvert -y --type cache --cachepool ${lv3}_cvol $vg/$lv2
|
||||
|
||||
vgremove -ff $vg
|
||||
|
||||
@@ -22,9 +22,9 @@ mkdir -p $mnt
|
||||
|
||||
aux prepare_devs 6 64
|
||||
|
||||
for i in `seq 1 16384`; do echo -n "A" >> fileA; done
|
||||
for i in `seq 1 16384`; do echo -n "B" >> fileB; done
|
||||
for i in `seq 1 16384`; do echo -n "C" >> fileC; done
|
||||
printf "%0.sA" {1..16384} >> fileA
|
||||
printf "%0.sB" {1..16384} >> fileB
|
||||
printf "%0.sC" {1..16384} >> fileC
|
||||
|
||||
# generate random data
|
||||
dd if=/dev/urandom of=randA bs=512K count=2
|
||||
@@ -109,6 +109,14 @@ _wait_recalc() {
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# TODO: There is some strange bug, first leg of RAID with integrity
|
||||
# enabled never gets in sync. I saw this in BB, but not when executing
|
||||
# the commands manually
|
||||
if test -z "$sync"; then
|
||||
echo "TEST WARNING: Resync of dm-integrity device '$checklv' failed"
|
||||
dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
|
||||
exit
|
||||
fi
|
||||
echo "timeout waiting for recalc"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ mkdir -p $mnt
|
||||
# raid1 LV needs to be extended to 512MB to test imeta being exended
|
||||
aux prepare_devs 4 600
|
||||
|
||||
for i in `seq 1 16384`; do echo -n "A" >> fileA; done
|
||||
for i in `seq 1 16384`; do echo -n "B" >> fileB; done
|
||||
for i in `seq 1 16384`; do echo -n "C" >> fileC; done
|
||||
printf "%0.sA" {1..16384} >> fileA
|
||||
printf "%0.sB" {1..16384} >> fileB
|
||||
printf "%0.sC" {1..16384} >> fileC
|
||||
|
||||
# generate random data
|
||||
dd if=/dev/urandom of=randA bs=512K count=2
|
||||
@@ -95,6 +95,14 @@ _wait_recalc() {
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# TODO: There is some strange bug, first leg of RAID with integrity
|
||||
# enabled never gets in sync. I saw this in BB, but not when executing
|
||||
# the commands manually
|
||||
if test -z "$sync"; then
|
||||
echo "TEST WARNING: Resync of dm-integrity device '$checklv' failed"
|
||||
dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
|
||||
exit
|
||||
fi
|
||||
echo "timeout waiting for recalc"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ mkdir -p $mnt
|
||||
|
||||
aux prepare_devs 5 64
|
||||
|
||||
for i in `seq 1 16384`; do echo -n "A" >> fileA; done
|
||||
for i in `seq 1 16384`; do echo -n "B" >> fileB; done
|
||||
for i in `seq 1 16384`; do echo -n "C" >> fileC; done
|
||||
printf "%0.sA" {1..16384} >> fileA
|
||||
printf "%0.sB" {1..16384} >> fileB
|
||||
printf "%0.sC" {1..16384} >> fileC
|
||||
|
||||
# generate random data
|
||||
dd if=/dev/urandom of=randA bs=512K count=2
|
||||
@@ -109,6 +109,14 @@ _wait_recalc() {
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# TODO: There is some strange bug, first leg of RAID with integrity
|
||||
# enabled never gets in sync. I saw this in BB, but not when executing
|
||||
# the commands manually
|
||||
if test -z "$sync"; then
|
||||
echo "TEST WARNING: Resync of dm-integrity device '$checklv' failed"
|
||||
dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
|
||||
exit
|
||||
fi
|
||||
echo "timeout waiting for recalc"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ mkdir -p $mnt
|
||||
|
||||
aux prepare_devs 5 64
|
||||
|
||||
for i in `seq 1 16384`; do echo -n "A" >> fileA; done
|
||||
for i in `seq 1 16384`; do echo -n "B" >> fileB; done
|
||||
for i in `seq 1 16384`; do echo -n "C" >> fileC; done
|
||||
printf "%0.sA" {1..16384} >> fileA
|
||||
printf "%0.sB" {1..16384} >> fileB
|
||||
printf "%0.sC" {1..16384} >> fileC
|
||||
|
||||
# generate random data
|
||||
dd if=/dev/urandom of=randA bs=512K count=2
|
||||
@@ -78,14 +78,14 @@ _test_fs_with_error() {
|
||||
dd if=$mnt/fileA of=tmp bs=1k
|
||||
ls -l tmp
|
||||
stat -c %s tmp
|
||||
diff fileA tmp
|
||||
cmp -b fileA tmp
|
||||
rm tmp
|
||||
|
||||
# read partial fileB which was corrupted
|
||||
not dd if=$mnt/fileB of=tmp bs=1k
|
||||
ls -l tmp
|
||||
stat -c %s tmp | grep 12288
|
||||
not diff fileB tmp
|
||||
not cmp -b fileB tmp
|
||||
rm tmp
|
||||
|
||||
umount $mnt
|
||||
@@ -118,14 +118,14 @@ _test_fs_with_raid() {
|
||||
dd if=$mnt/fileA of=tmp bs=1k
|
||||
ls -l tmp
|
||||
stat -c %s tmp | grep 16384
|
||||
diff fileA tmp
|
||||
cmp -b fileA tmp
|
||||
rm tmp
|
||||
|
||||
# read complete fileB, corruption is corrected by raid
|
||||
dd if=$mnt/fileB of=tmp bs=1k
|
||||
ls -l tmp
|
||||
stat -c %s tmp | grep 16384
|
||||
diff fileB tmp
|
||||
cmp -b fileB tmp
|
||||
rm tmp
|
||||
|
||||
umount $mnt
|
||||
@@ -161,15 +161,15 @@ _add_more_data_to_mnt() {
|
||||
}
|
||||
|
||||
_verify_data_on_mnt() {
|
||||
diff randA $mnt/randA
|
||||
diff randB $mnt/randB
|
||||
diff randC $mnt/randC
|
||||
diff fileA $mnt/1/fileA
|
||||
diff fileB $mnt/1/fileB
|
||||
diff fileC $mnt/1/fileC
|
||||
diff fileA $mnt/2/fileA
|
||||
diff fileB $mnt/2/fileB
|
||||
diff fileC $mnt/2/fileC
|
||||
cmp -b randA $mnt/randA
|
||||
cmp -b randB $mnt/randB
|
||||
cmp -b randC $mnt/randC
|
||||
cmp -b fileA $mnt/1/fileA
|
||||
cmp -b fileB $mnt/1/fileB
|
||||
cmp -b fileC $mnt/1/fileC
|
||||
cmp -b fileA $mnt/2/fileA
|
||||
cmp -b fileB $mnt/2/fileB
|
||||
cmp -b fileC $mnt/2/fileC
|
||||
}
|
||||
|
||||
_verify_data_on_lv() {
|
||||
@@ -204,6 +204,14 @@ _wait_recalc() {
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# TODO: There is some strange bug, first leg of RAID with integrity
|
||||
# enabled never gets in sync. I saw this in BB, but not when executing
|
||||
# the commands manually
|
||||
if test -z "$sync"; then
|
||||
echo "TEST WARNING: Resync of dm-integrity device '$checklv' failed"
|
||||
dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
|
||||
exit
|
||||
fi
|
||||
echo "timeout waiting for recalc"
|
||||
return 1
|
||||
}
|
||||
@@ -213,6 +221,8 @@ _wait_recalc() {
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid1 -m1 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
@@ -221,6 +231,9 @@ vgremove -ff $vg
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid1 -m2 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_wait_recalc $vg/${lv1}_rimage_2
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
@@ -229,6 +242,9 @@ vgremove -ff $vg
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid4 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_wait_recalc $vg/${lv1}_rimage_2
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
@@ -237,6 +253,9 @@ vgremove -ff $vg
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid5 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_wait_recalc $vg/${lv1}_rimage_2
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
@@ -245,6 +264,11 @@ vgremove -ff $vg
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid6 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_wait_recalc $vg/${lv1}_rimage_2
|
||||
_wait_recalc $vg/${lv1}_rimage_3
|
||||
_wait_recalc $vg/${lv1}_rimage_4
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
@@ -253,6 +277,10 @@ vgremove -ff $vg
|
||||
|
||||
_prepare_vg
|
||||
lvcreate --type raid10 --raidintegrity y -n $lv1 -l 8 $vg
|
||||
_wait_recalc $vg/${lv1}_rimage_0
|
||||
_wait_recalc $vg/${lv1}_rimage_1
|
||||
_wait_recalc $vg/${lv1}_rimage_2
|
||||
_wait_recalc $vg/${lv1}_rimage_3
|
||||
_test_fs_with_raid
|
||||
lvchange -an $vg/$lv1
|
||||
lvconvert --raidintegrity n $vg/$lv1
|
||||
|
||||
@@ -50,17 +50,6 @@ check vg_field $vg1 systemid "$SID"
|
||||
vgremove $vg1
|
||||
fi
|
||||
|
||||
## appmachineid
|
||||
lvm version > lvmver
|
||||
if grep app-machineid lvmver; then
|
||||
aux lvmconf "global/system_id_source = appmachineid"
|
||||
lvm systemid | awk '{ print $3 }' > sid_lvm
|
||||
vgcreate $vg1 "$dev1"
|
||||
vgs -o systemid --noheadings $vg1 | awk '{print $1}' > sid_vg
|
||||
diff sid_lvm sid_vg
|
||||
vgremove $vg1
|
||||
fi
|
||||
|
||||
## uname
|
||||
|
||||
SID1=$(uname -n)
|
||||
|
||||
@@ -17,9 +17,15 @@ SKIP_WITH_LVMPOLLD=1
|
||||
|
||||
. lib/inittest
|
||||
|
||||
clean_thin_()
|
||||
{
|
||||
aux udev_wait
|
||||
dmsetup remove "$THIN" || { sleep .5 ; dmsetup remove "$THIN" ; }
|
||||
}
|
||||
|
||||
cleanup_mounted_and_teardown()
|
||||
{
|
||||
dmsetup remove $THIN || true
|
||||
clean_thin_ || true
|
||||
vgremove -ff $vg
|
||||
aux teardown
|
||||
}
|
||||
@@ -56,7 +62,7 @@ dmsetup create "$THIN" --table "0 40960 thin $DM_DEV_DIR/mapper/$POOL 0"
|
||||
|
||||
mkfs.ext4 "$DM_DEV_DIR/mapper/$THIN"
|
||||
|
||||
dmsetup remove "$THIN"
|
||||
clean_thin_
|
||||
|
||||
lvchange -an $vg/pool
|
||||
|
||||
@@ -70,3 +76,5 @@ lvchange -ay $vg/pool
|
||||
dmsetup create "$THIN" --table "0 40960 thin $DM_DEV_DIR/mapper/$POOL 0"
|
||||
|
||||
fsck -n "$DM_DEV_DIR/mapper/$THIN"
|
||||
|
||||
# exit calls cleanup_mounted_and_teardown
|
||||
|
||||
20
tools/args.h
20
tools/args.h
@@ -199,6 +199,15 @@ arg(detachprofile_ARG, '\0', "detachprofile", 0, 0, 0,
|
||||
"Detaches a metadata profile from a VG or LV.\n"
|
||||
"See \\fBlvm.conf\\fP(5) for more information about profiles.\n")
|
||||
|
||||
arg(deviceid_ARG, '\0', "deviceid", string_VAL, 0, 0,
|
||||
"A device ID with a format determined by --deviceidtype.")
|
||||
|
||||
arg(deviceidtype_ARG, '\0', "deviceidtype", string_VAL, 0, 0,
|
||||
"A device ID type: sys_wwid, sys_serial, mpath_uuid.")
|
||||
|
||||
arg(devicesfile_ARG, '\0', "devicesfile", string_VAL, 0, 0,
|
||||
"The file listing device IDs that LVM should use.")
|
||||
|
||||
arg(discards_ARG, '\0', "discards", discards_VAL, 0, 0,
|
||||
"Specifies how the device-mapper thin pool layer in the kernel should\n"
|
||||
"handle discards.\n"
|
||||
@@ -1428,7 +1437,16 @@ arg(thin_ARG, 'T', "thin", 0, 0, 0,
|
||||
"See \\fBlvmthin\\fP(7) for more information about LVM thin provisioning.\n")
|
||||
|
||||
arg(updatemetadata_ARG, '\0', "updatemetadata", 0, 0, 0,
|
||||
"Update VG metadata to correct problems.\n")
|
||||
"Update VG metadata to correct problems.\n"
|
||||
"If VG metadata was updated while a PV was missing, and the PV\n"
|
||||
"reappears with an old version of metadata, then this option\n"
|
||||
"(or any other command that writes metadata) will update the\n"
|
||||
"metadata on the previously missing PV. If a PV was removed\n"
|
||||
"from a VG while it was missing, and the PV reappears, using\n"
|
||||
"this option will clear the outdated metadata from the previously\n"
|
||||
"missing PV. If metadata text is damaged on one PV, using this\n"
|
||||
"option will replace the damaged metadata text. For more severe\n"
|
||||
"damage, e.g. with headers, see \\fBpvck\\fP(8).\n")
|
||||
|
||||
arg(uuid_ARG, 'u', "uuid", 0, 0, 0,
|
||||
"#pvchange\n"
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
#
|
||||
OO_ALL: --commandprofile String, --config String, --debug,
|
||||
--driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet,
|
||||
--verbose, --version, --yes, --test
|
||||
--verbose, --version, --yes, --test, --devicesfile String
|
||||
|
||||
#
|
||||
# options for pvs, lvs, vgs, fullreport
|
||||
@@ -1479,7 +1479,8 @@ OO: --dataalignment SizeKB, --dataalignmentoffset SizeKB, --bootloaderareasize S
|
||||
--force, --labelsector Number, --metadatatype MetadataType,
|
||||
--pvmetadatacopies MetadataCopiesPV, --metadatasize SizeMB,
|
||||
--metadataignore Bool, --norestorefile, --setphysicalvolumesize SizeMB,
|
||||
--reportformat ReportFmt, --restorefile String, --uuidstr String, --zero Bool
|
||||
--reportformat ReportFmt, --restorefile String, --uuidstr String, --zero Bool,
|
||||
--deviceidtype String, --deviceid String
|
||||
ID: pvcreate_general
|
||||
RULE: --norestorefile not --restorefile
|
||||
RULE: --bootloaderareasize not --restorefile
|
||||
|
||||
@@ -2319,7 +2319,8 @@ static void _print_val_man(struct command_name *cname, int opt_enum, int val_enu
|
||||
}
|
||||
|
||||
if (strchr(str, '|')) {
|
||||
line = strdup(str);
|
||||
if (!(line = strdup(str)))
|
||||
return;
|
||||
_split_line(line, &line_argc, line_argv, '|');
|
||||
for (i = 0; i < line_argc; i++) {
|
||||
if (i)
|
||||
@@ -3606,9 +3607,12 @@ int main(int argc, char *argv[])
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
cmdname = strdup(argv[optind++]);
|
||||
else {
|
||||
if (optind < argc) {
|
||||
if (!(cmdname = strdup(argv[optind++]))) {
|
||||
log_error("Out of memory.");
|
||||
goto out_free;
|
||||
}
|
||||
} else {
|
||||
log_error("Missing command name.");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
@@ -1319,6 +1319,7 @@ static int _raid4_conversion_supported(struct logical_volume *lv, struct lvconve
|
||||
static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *lp)
|
||||
{
|
||||
int image_count = 0;
|
||||
int images_reduced = 0;
|
||||
struct cmd_context *cmd = lv->vg->cmd;
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
@@ -1357,6 +1358,8 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
|
||||
else
|
||||
image_count = lp->mirrors + 1;
|
||||
|
||||
images_reduced = (image_count < lv_raid_image_count(lv));
|
||||
|
||||
if (image_count < 1) {
|
||||
log_error("Unable to %s images by specified amount.",
|
||||
lp->keep_mimages ? "split" : "reduce");
|
||||
@@ -1400,7 +1403,7 @@ static int _lvconvert_raid(struct logical_volume *lv, struct lvconvert_params *l
|
||||
lp->region_size : seg->region_size , lp->pvh))
|
||||
return_0;
|
||||
|
||||
if (lv_raid_has_integrity(lv)) {
|
||||
if (lv_raid_has_integrity(lv) && !images_reduced) {
|
||||
struct integrity_settings *isettings = NULL;
|
||||
if (!lv_get_raid_integrity_settings(lv, &isettings))
|
||||
return_0;
|
||||
@@ -4264,6 +4267,11 @@ static int _lvconvert_cachevol_attach_single(struct cmd_context *cmd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lv_is_cache_vol(cachevol_lv)) {
|
||||
log_error("LV %s is already used as a cachevol.", display_lvname(cachevol_lv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ensure the LV is not active elsewhere. */
|
||||
if (!lockd_lv(cmd, lv, "ex", 0))
|
||||
goto_out;
|
||||
@@ -4347,6 +4355,11 @@ static int _lvconvert_cachepool_attach_single(struct cmd_context *cmd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lv_is_cache_vol(cachepool_lv)) {
|
||||
log_error("LV %s is already used as a cachevol.", display_lvname(cachepool_lv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cachepool_lv == lv) {
|
||||
log_error("Use a different LV for cache pool LV and cache LV %s.",
|
||||
display_lvname(cachepool_lv));
|
||||
@@ -5589,7 +5602,8 @@ static struct logical_volume *_lv_writecache_create(struct cmd_context *cmd,
|
||||
|
||||
memcpy(&seg->writecache_settings, settings, sizeof(struct writecache_settings));
|
||||
|
||||
add_seg_to_segs_using_this_lv(lv_fast, seg);
|
||||
if (!add_seg_to_segs_using_this_lv(lv_fast, seg))
|
||||
return_NULL;
|
||||
|
||||
return lv_wcorig;
|
||||
}
|
||||
@@ -5628,6 +5642,11 @@ static int _lvconvert_writecache_attach_single(struct cmd_context *cmd,
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (lv_is_cache_vol(lv_fast)) {
|
||||
log_error("LV %s is already used as a cachevol.", display_lvname(lv_fast));
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* To permit this we need to check the block size of the fs using lv
|
||||
* (recently in libblkid) so that we can use a matching writecache
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "lvm2cmdline.h"
|
||||
#include "lib/label/label.h"
|
||||
#include "lib/device/device_id.h"
|
||||
#include "lvm-version.h"
|
||||
#include "lib/locking/lvmlockd.h"
|
||||
|
||||
@@ -2477,6 +2478,11 @@ static int _get_current_settings(struct cmd_context *cmd)
|
||||
cmd->record_historical_lvs = find_config_tree_bool(cmd, metadata_record_lvs_history_CFG, NULL) ?
|
||||
(arg_is_set(cmd, nohistory_ARG) ? 0 : 1) : 0;
|
||||
|
||||
if (arg_is_set(cmd, devicesfile_ARG))
|
||||
cmd->devices_file = arg_str_value(cmd, devicesfile_ARG, "");
|
||||
if (devices_file_valid(cmd))
|
||||
cmd->enable_device_ids = 1;
|
||||
|
||||
/*
|
||||
* This is set to zero by process_each which wants to print errors
|
||||
* itself rather than having them printed in vg_read.
|
||||
|
||||
@@ -3065,11 +3065,9 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
label_scan_setup_bcache();
|
||||
|
||||
if (arg_is_set(cmd, dump_ARG)) {
|
||||
if ((dump = arg_str_value(cmd, dump_ARG, NULL))) {
|
||||
cmd->use_hints = 0;
|
||||
|
||||
dump = arg_str_value(cmd, dump_ARG, NULL);
|
||||
|
||||
if (!strcmp(dump, "metadata"))
|
||||
ret = _dump_metadata(cmd, dump, &set, labelsector, dev, def, PRINT_CURRENT, 0);
|
||||
|
||||
@@ -3096,11 +3094,9 @@ int pvck(struct cmd_context *cmd, int argc, char **argv)
|
||||
return ECMD_PROCESSED;
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, repairtype_ARG)) {
|
||||
if ((repair = arg_str_value(cmd, repairtype_ARG, NULL))) {
|
||||
cmd->use_hints = 0;
|
||||
|
||||
repair = arg_str_value(cmd, repairtype_ARG, NULL);
|
||||
|
||||
if (!strcmp(repair, "label_header"))
|
||||
ret = _repair_label_header(cmd, repair, &set, labelsector, dev);
|
||||
|
||||
|
||||
@@ -820,6 +820,11 @@ static void _online_pvscan_all_devs(struct cmd_context *cmd,
|
||||
struct device *dev;
|
||||
const char *pvid_without_metadata;
|
||||
|
||||
/*
|
||||
* TODO: label_scan() calls setup_devices(), but pvscan --cache is a
|
||||
* special case in which setup_devices() has already been called.
|
||||
* So, we could improve things by suppressing the second setup_devices().
|
||||
*/
|
||||
lvmcache_label_scan(cmd);
|
||||
|
||||
if (!(iter = dev_iter_create(cmd->filter, 1))) {
|
||||
@@ -1254,8 +1259,8 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
|
||||
|
||||
_online_dir_setup();
|
||||
|
||||
/* Creates a list of dev names from /dev, sysfs, etc; does not read any. */
|
||||
dev_cache_scan();
|
||||
/* Creates a list of available devices, does not open or read any. */
|
||||
setup_devices(cmd);
|
||||
|
||||
if (cmd->md_component_detection && !cmd->use_full_md_check &&
|
||||
!strcmp(cmd->md_component_checks, "auto") &&
|
||||
|
||||
552
tools/toollib.c
552
tools/toollib.c
@@ -16,6 +16,7 @@
|
||||
#include "tools.h"
|
||||
#include "lib/format_text/format-text.h"
|
||||
#include "lib/label/hints.h"
|
||||
#include "lib/device/device_id.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
@@ -4719,111 +4720,84 @@ static struct pvcreate_device *_pvcreate_list_find_name(struct dm_list *devices,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this function decides that a arg_devices entry cannot be used, but the
|
||||
* command might be able to continue without it, then it moves that entry from
|
||||
* arg_devices to arg_fail.
|
||||
*
|
||||
* If this function decides that an arg_devices entry could be used (possibly
|
||||
* requiring a prompt), then it moves the entry from arg_devices to arg_process.
|
||||
*
|
||||
* Any arg_devices entries that are not moved to arg_fail or arg_process were
|
||||
* not found. The caller will decide if the command can continue if any
|
||||
* arg_devices entries were not found, or if any were moved to arg_fail.
|
||||
*
|
||||
* This check does not need to look at PVs in foreign, shared or clustered VGs.
|
||||
* If pvcreate/vgcreate/vgextend specifies a device in a
|
||||
* foreign/shared/clustered VG, that VG will not be processed by this function,
|
||||
* and the arg will be reported as not found.
|
||||
*/
|
||||
static int _pvcreate_check_single(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
struct processing_handle *handle)
|
||||
static int _pvcreate_check_used(struct cmd_context *cmd,
|
||||
struct pvcreate_params *pp,
|
||||
struct pvcreate_device *pd)
|
||||
{
|
||||
struct pvcreate_params *pp = (struct pvcreate_params *) handle->custom_handle;
|
||||
struct pvcreate_device *pd;
|
||||
struct pvcreate_prompt *prompt;
|
||||
uint64_t size = 0;
|
||||
uint64_t new_size = 0;
|
||||
int need_size_prompt = 0;
|
||||
int need_vg_prompt = 0;
|
||||
int found = 0;
|
||||
struct lvmcache_info *info;
|
||||
const char *vgname;
|
||||
|
||||
if (!pv->dev)
|
||||
return 1;
|
||||
log_debug("Checking %s for pvcreate %.32s.",
|
||||
dev_name(pd->dev), pd->dev->pvid[0] ? pd->dev->pvid : "");
|
||||
|
||||
/*
|
||||
* Check if one of the command args in arg_devices
|
||||
* matches this device.
|
||||
* Since the device is likely not a PV yet, it was probably not
|
||||
* scanned by label_scan at the start of the command, so that
|
||||
* needs to be done first to find if there's a PV label or metadata
|
||||
* on it. If there's a PV label, it sets dev->pvid.
|
||||
* If a VG is using the dev, it adds basic VG info for it to
|
||||
* lvmcache.
|
||||
*/
|
||||
dm_list_iterate_items(pd, &pp->arg_devices) {
|
||||
if (pd->dev != pv->dev)
|
||||
continue;
|
||||
label_read(pd->dev);
|
||||
|
||||
if (pv->dev->pvid[0])
|
||||
strncpy(pd->pvid, pv->dev->pvid, ID_LEN);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the uuid specified for the new PV is used by another PV.
|
||||
*/
|
||||
if (!found && pv->dev && pp->uuid_str && id_equal(&pv->id, &pp->pva.id)) {
|
||||
log_error("UUID %s already in use on \"%s\".", pp->uuid_str, pv_dev_name(pv));
|
||||
pp->check_failed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
if (!pd->dev->pvid[0]) {
|
||||
log_debug("Check pvcreate arg %s no PVID found", dev_name(pd->dev));
|
||||
pd->is_not_pv = 1;
|
||||
return 1;
|
||||
|
||||
log_debug("Checking pvcreate arg %s which has existing PVID: %.32s.",
|
||||
pv_dev_name(pv), pv->dev->pvid[0] ? pv->dev->pvid : "<none>");
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow using a device with duplicates.
|
||||
*/
|
||||
if (lvmcache_pvid_in_unused_duplicates(pd->dev->pvid)) {
|
||||
log_error("Cannot use device %s with duplicates.", pd->name);
|
||||
log_error("Cannot use device %s with duplicates.", dev_name(pd->dev));
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(pd->dev->pvid, pd->dev, 0))) {
|
||||
/* FIXME: is this an orphan PV if there's a PVID? */
|
||||
log_debug("Check pvcreate arg %s no info.", dev_name(pd->dev));
|
||||
pd->is_not_pv = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
vgname = lvmcache_vgname_from_info(info);
|
||||
|
||||
/*
|
||||
* What kind of device is this: an orphan PV, an uninitialized/unused
|
||||
* device, a PV used in a VG.
|
||||
*/
|
||||
if (vg && !is_orphan_vg(vg->name)) {
|
||||
if (vgname && !is_orphan_vg(vgname)) {
|
||||
/* Device is a PV used in a VG. */
|
||||
log_debug("Found pvcreate arg %s: pv is used in %s.", pd->name, vg->name);
|
||||
log_debug("Check pvcreate arg %s found vg %s.", dev_name(pd->dev), vgname);
|
||||
pd->is_vg_pv = 1;
|
||||
pd->vg_name = dm_pool_strdup(cmd->mem, vg->name);
|
||||
} else if (vg && is_orphan_vg(vg->name)) {
|
||||
if (is_used_pv(pv)) {
|
||||
pd->vg_name = dm_pool_strdup(cmd->mem, vgname);
|
||||
} else if (!vgname || (vgname && is_orphan_vg(vgname))) {
|
||||
uint32_t ext_flags = lvmcache_ext_flags(info);
|
||||
if (ext_flags & PV_EXT_USED) {
|
||||
/* Device is used in an unknown VG. */
|
||||
log_debug("Found pvcreate arg %s: PV is used in unknown VG.", pd->name);
|
||||
log_debug("Check pvcreate arg %s found EXT_USED flag.", dev_name(pd->dev));
|
||||
pd->is_used_unknown_pv = 1;
|
||||
} else {
|
||||
/* Device is an orphan PV. */
|
||||
log_debug("Found pvcreate arg %s: PV is orphan in %s.", pd->name, vg->name);
|
||||
log_debug("Check pvcreate arg %s is orphan.", dev_name(pd->dev));
|
||||
pd->is_orphan_pv = 1;
|
||||
}
|
||||
|
||||
pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME;
|
||||
} else {
|
||||
log_debug("Found pvcreate arg %s: device is not a PV.", pd->name);
|
||||
/* Device is not a PV. */
|
||||
pd->is_not_pv = 1;
|
||||
|
||||
}
|
||||
|
||||
if (arg_is_set(cmd, setphysicalvolumesize_ARG)) {
|
||||
new_size = arg_uint64_value(cmd, setphysicalvolumesize_ARG, UINT64_C(0));
|
||||
|
||||
if (!dev_get_size(pv->dev, &size)) {
|
||||
log_error("Can't get device size of %s.", pv_dev_name(pv));
|
||||
if (!dev_get_size(pd->dev, &size)) {
|
||||
log_error("Can't get device size of %s.", dev_name(pd->dev));
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return 1;
|
||||
}
|
||||
@@ -4844,26 +4818,22 @@ static int _pvcreate_check_single(struct cmd_context *cmd,
|
||||
else
|
||||
need_vg_prompt = 1;
|
||||
|
||||
if (!need_size_prompt && !need_vg_prompt) {
|
||||
pd->dev = pv->dev;
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
if (!need_size_prompt && !need_vg_prompt)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(prompt = dm_pool_zalloc(cmd->mem, sizeof(*prompt)))) {
|
||||
log_error("prompt alloc failed.");
|
||||
pp->check_failed = 1;
|
||||
return 0;
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return_0;
|
||||
}
|
||||
prompt->dev = pd->dev;
|
||||
prompt->pv_name = dm_pool_strdup(cmd->mem, pd->name);
|
||||
prompt->pv_name = dm_pool_strdup(cmd->mem, dev_name(pd->dev));
|
||||
prompt->size = size;
|
||||
prompt->new_size = new_size;
|
||||
|
||||
if (pd->is_used_unknown_pv)
|
||||
prompt->vg_name_unknown = 1;
|
||||
else if (need_vg_prompt)
|
||||
prompt->vg_name = dm_pool_strdup(cmd->mem, vg->name);
|
||||
prompt->vg_name = dm_pool_strdup(cmd->mem, vgname);
|
||||
|
||||
if (need_size_prompt)
|
||||
prompt->type |= PROMPT_PVCREATE_DEV_SIZE;
|
||||
@@ -4872,149 +4842,44 @@ static int _pvcreate_check_single(struct cmd_context *cmd,
|
||||
prompt->type |= PROMPT_PVCREATE_PV_IN_VG;
|
||||
|
||||
dm_list_add(&pp->prompts, &prompt->list);
|
||||
pd->dev = pv->dev;
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This repeats the first check -- devices should be found, and should not have
|
||||
* changed since the first check. If they were changed/used while the orphans
|
||||
* lock was not held (during prompting), then they can't be used any more and
|
||||
* are moved to arg_fail. If they are not found by this loop, that also
|
||||
* disqualifies them from being used. Each arg_confirm entry that's found and
|
||||
* is ok, is moved to arg_process. Those not found will remain in arg_confirm.
|
||||
*
|
||||
* This check does not need to look in foreign/shared/clustered VGs. If a
|
||||
* device from arg_confirm was used in a foreign/shared/clustered VG during the
|
||||
* prompts, then it will not be found during this check.
|
||||
*/
|
||||
|
||||
static int _pv_confirm_single(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
struct processing_handle *handle)
|
||||
static int _pvremove_check_used(struct cmd_context *cmd,
|
||||
struct pvcreate_params *pp,
|
||||
struct pvcreate_device *pd)
|
||||
{
|
||||
struct pvcreate_params *pp = (struct pvcreate_params *) handle->custom_handle;
|
||||
struct pvcreate_device *pd;
|
||||
int found = 0;
|
||||
|
||||
dm_list_iterate_items(pd, &pp->arg_confirm) {
|
||||
if (pd->dev != pv->dev)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* What kind of device is this: an orphan PV, an uninitialized/unused
|
||||
* device, a PV used in a VG.
|
||||
*/
|
||||
if (vg && !is_orphan_vg(vg->name)) {
|
||||
/* Device is a PV used in a VG. */
|
||||
|
||||
if (pd->is_orphan_pv || pd->is_not_pv || pd->is_used_unknown_pv) {
|
||||
/* In check_single it was an orphan or unused. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv && pd->vg_name && strcmp(pd->vg_name, vg->name)) {
|
||||
/* In check_single it was in a different VG. */
|
||||
goto fail;
|
||||
}
|
||||
} else if (is_orphan(pv)) {
|
||||
/* Device is an orphan PV. */
|
||||
|
||||
if (pd->is_not_pv) {
|
||||
/* In check_single it was not a PV. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv) {
|
||||
/* In check_single it was in a VG. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (is_used_pv(pv) != pd->is_used_unknown_pv) {
|
||||
/* In check_single it was different. */
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* Device is not a PV. */
|
||||
if (pd->is_orphan_pv || pd->is_used_unknown_pv) {
|
||||
/* In check_single it was an orphan PV. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv) {
|
||||
/* In check_single it was in a VG. */
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device is unchanged from check_single. */
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
log_error("Cannot use device %s: it changed during prompt.", pd->name);
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _pvremove_check_single(struct cmd_context *cmd,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv,
|
||||
struct processing_handle *handle)
|
||||
{
|
||||
struct pvcreate_params *pp = (struct pvcreate_params *) handle->custom_handle;
|
||||
struct pvcreate_device *pd;
|
||||
struct pvcreate_prompt *prompt;
|
||||
int found = 0;
|
||||
|
||||
if (!pv->dev)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Check if one of the command args in arg_devices
|
||||
* matches this device.
|
||||
*/
|
||||
dm_list_iterate_items(pd, &pp->arg_devices) {
|
||||
if (pd->dev != pv->dev)
|
||||
continue;
|
||||
|
||||
if (pv->dev->pvid[0])
|
||||
strncpy(pd->pvid, pv->dev->pvid, ID_LEN);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return 1;
|
||||
|
||||
log_debug("Checking device %s for pvremove %.32s.",
|
||||
pv_dev_name(pv), pv->dev->pvid[0] ? pv->dev->pvid : "");
|
||||
struct lvmcache_info *info;
|
||||
const char *vgname = NULL;
|
||||
|
||||
log_debug("Checking %s for pvremove %.32s.",
|
||||
dev_name(pd->dev), pd->dev->pvid[0] ? pd->dev->pvid : "");
|
||||
|
||||
/*
|
||||
* Is there a pv here already?
|
||||
* If not, this is an error unless you used -f.
|
||||
*/
|
||||
if (!lvmcache_has_dev_info(pv->dev)) {
|
||||
if (pp->force) {
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
|
||||
if (!pd->dev->pvid[0]) {
|
||||
log_debug("Check pvremove arg %s no PVID found", dev_name(pd->dev));
|
||||
if (pp->force)
|
||||
return 1;
|
||||
} else {
|
||||
pd->is_not_pv = 1;
|
||||
}
|
||||
pd->is_not_pv = 1;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(pd->dev->pvid, pd->dev, 0))) {
|
||||
/* FIXME: is this an orphan PV if there's a PVID? */
|
||||
log_debug("Check pvremove arg %s no info.", dev_name(pd->dev));
|
||||
if (pp->force)
|
||||
return 1;
|
||||
pd->is_not_pv = 1;
|
||||
}
|
||||
|
||||
if (info)
|
||||
vgname = lvmcache_vgname_from_info(info);
|
||||
|
||||
/*
|
||||
* What kind of device is this: an orphan PV, an uninitialized/unused
|
||||
* device, a PV used in a VG.
|
||||
@@ -5022,36 +4887,30 @@ static int _pvremove_check_single(struct cmd_context *cmd,
|
||||
|
||||
if (pd->is_not_pv) {
|
||||
/* Device is not a PV. */
|
||||
log_debug("Found pvremove arg %s: device is not a PV.", pd->name);
|
||||
log_debug("Check pvremove arg %s device is not a PV.", dev_name(pd->dev));
|
||||
|
||||
} else if (vg && !is_orphan_vg(vg->name)) {
|
||||
} else if (vgname && !is_orphan_vg(vgname)) {
|
||||
/* Device is a PV used in a VG. */
|
||||
log_debug("Found pvremove arg %s: pv is used in %s.", pd->name, vg->name);
|
||||
log_debug("Check pvremove arg %s found vg %s.", dev_name(pd->dev), vgname);
|
||||
pd->is_vg_pv = 1;
|
||||
pd->vg_name = dm_pool_strdup(cmd->mem, vg->name);
|
||||
pd->vg_name = dm_pool_strdup(cmd->mem, vgname);
|
||||
|
||||
} else if (vg && is_orphan_vg(vg->name)) {
|
||||
if (is_used_pv(pv)) {
|
||||
} else if (info && (!vgname || (vgname && is_orphan_vg(vgname)))) {
|
||||
uint32_t ext_flags = lvmcache_ext_flags(info);
|
||||
if (ext_flags & PV_EXT_USED) {
|
||||
/* Device is used in an unknown VG. */
|
||||
log_debug("Found pvremove arg %s: pv is used in unknown VG.", pd->name);
|
||||
log_debug("Check pvremove arg %s found EXT_USED flag.", dev_name(pd->dev));
|
||||
pd->is_used_unknown_pv = 1;
|
||||
} else {
|
||||
/* Device is an orphan PV. */
|
||||
log_debug("Found pvremove arg %s: pv is orphan in %s.", pd->name, vg->name);
|
||||
log_debug("Check pvremove arg %s is orphan.", dev_name(pd->dev));
|
||||
pd->is_orphan_pv = 1;
|
||||
}
|
||||
|
||||
pp->orphan_vg_name = FMT_TEXT_ORPHAN_VG_NAME;
|
||||
} else {
|
||||
/* FIXME: is it possible to reach here? */
|
||||
log_debug("Found pvremove arg %s: device is not a PV.", pd->name);
|
||||
/* Device is not a PV. */
|
||||
pd->is_not_pv = 1;
|
||||
}
|
||||
|
||||
if (pd->is_not_pv) {
|
||||
pd->dev = pv->dev;
|
||||
log_error("No PV found on device %s.", pd->name);
|
||||
log_error("No PV found on device %s.", dev_name(pd->dev));
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return 1;
|
||||
}
|
||||
@@ -5060,11 +4919,8 @@ static int _pvremove_check_single(struct cmd_context *cmd,
|
||||
* pvremove is being run on this device, and it's not a PV,
|
||||
* or is an orphan PV. Neither case requires a prompt.
|
||||
*/
|
||||
if (pd->is_orphan_pv) {
|
||||
pd->dev = pv->dev;
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
if (pd->is_orphan_pv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* pvremove is being run on this device, but the device is in a VG.
|
||||
@@ -5072,22 +4928,104 @@ static int _pvremove_check_single(struct cmd_context *cmd,
|
||||
*/
|
||||
|
||||
if (!(prompt = dm_pool_zalloc(cmd->mem, sizeof(*prompt)))) {
|
||||
log_error("prompt alloc failed.");
|
||||
pp->check_failed = 1;
|
||||
return 0;
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return_0;
|
||||
}
|
||||
prompt->dev = pd->dev;
|
||||
prompt->pv_name = dm_pool_strdup(cmd->mem, pd->name);
|
||||
prompt->pv_name = dm_pool_strdup(cmd->mem, dev_name(pd->dev));
|
||||
if (pd->is_used_unknown_pv)
|
||||
prompt->vg_name_unknown = 1;
|
||||
else
|
||||
prompt->vg_name = dm_pool_strdup(cmd->mem, vg->name);
|
||||
prompt->vg_name = dm_pool_strdup(cmd->mem, vgname);
|
||||
prompt->type |= PROMPT_PVREMOVE_PV_IN_VG;
|
||||
dm_list_add(&pp->prompts, &prompt->list);
|
||||
|
||||
pd->dev = pv->dev;
|
||||
dm_list_move(&pp->arg_process, &pd->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _confirm_check_used(struct cmd_context *cmd,
|
||||
struct pvcreate_params *pp,
|
||||
struct pvcreate_device *pd)
|
||||
{
|
||||
struct lvmcache_info *info = NULL;
|
||||
const char *vgname = NULL;
|
||||
int is_not_pv = 0;
|
||||
|
||||
log_debug("Checking %s to confirm %.32s.",
|
||||
dev_name(pd->dev), pd->dev->pvid[0] ? pd->dev->pvid : "");
|
||||
|
||||
if (!pd->dev->pvid[0]) {
|
||||
log_debug("Check confirm arg %s no PVID found", dev_name(pd->dev));
|
||||
is_not_pv = 1;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(pd->dev->pvid, pd->dev, 0))) {
|
||||
log_debug("Check confirm arg %s no info.", dev_name(pd->dev));
|
||||
is_not_pv = 1;
|
||||
}
|
||||
|
||||
if (info)
|
||||
vgname = lvmcache_vgname_from_info(info);
|
||||
|
||||
|
||||
/*
|
||||
* What kind of device is this: an orphan PV, an uninitialized/unused
|
||||
* device, a PV used in a VG.
|
||||
*/
|
||||
if (vgname && !is_orphan_vg(vgname)) {
|
||||
/* Device is a PV used in a VG. */
|
||||
|
||||
if (pd->is_orphan_pv || pd->is_not_pv || pd->is_used_unknown_pv) {
|
||||
/* In first check it was an orphan or unused. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv && pd->vg_name && strcmp(pd->vg_name, vgname)) {
|
||||
/* In first check it was in a different VG. */
|
||||
goto fail;
|
||||
}
|
||||
} else if (info && (!vgname || is_orphan_vg(vgname))) {
|
||||
uint32_t ext_flags = lvmcache_ext_flags(info);
|
||||
|
||||
/* Device is an orphan PV. */
|
||||
|
||||
if (pd->is_not_pv) {
|
||||
/* In first check it was not a PV. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv) {
|
||||
/* In first check it was in a VG. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ext_flags & PV_EXT_USED) && !pd->is_used_unknown_pv) {
|
||||
/* In first check it was different. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(ext_flags & PV_EXT_USED) && pd->is_used_unknown_pv) {
|
||||
/* In first check it was different. */
|
||||
goto fail;
|
||||
}
|
||||
} else if (is_not_pv) {
|
||||
/* Device is not a PV. */
|
||||
if (pd->is_orphan_pv || pd->is_used_unknown_pv) {
|
||||
/* In first check it was an orphan PV. */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pd->is_vg_pv) {
|
||||
/* In first check it was in a VG. */
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
log_error("Cannot use device %s: it changed during prompt.", dev_name(pd->dev));
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5128,6 +5066,7 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
unsigned int physical_block_size, logical_block_size;
|
||||
unsigned int prev_pbs = 0, prev_lbs = 0;
|
||||
int must_use_all = (cmd->cname->flags & MUST_USE_ALL_ARGS);
|
||||
int unlocked_for_prompts = 0;
|
||||
int found;
|
||||
unsigned i;
|
||||
|
||||
@@ -5162,9 +5101,30 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
|
||||
/*
|
||||
* Translate arg names into struct device's.
|
||||
*
|
||||
* TODO: do we want to add a config setting that would disable this
|
||||
* ability of pvcreate to use devs outside of the devices_file?
|
||||
* If devices_file is to be more strict in allowing access to devs,
|
||||
* e.g. applied to pvcreate, then a user would need to add the new
|
||||
* device to devices_file prior to running pvcreate on it.
|
||||
*/
|
||||
dm_list_iterate_items(pd, &pp->arg_devices)
|
||||
pd->dev = dev_cache_get(cmd, pd->name, cmd->filter);
|
||||
dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) {
|
||||
pd->dev = dev_cache_get(cmd, pd->name,
|
||||
cmd->enable_device_ids ? NULL : cmd->filter);
|
||||
if (!pd->dev) {
|
||||
log_print("No device found for %s", pd->name);
|
||||
dm_list_del(&pd->list);
|
||||
dm_list_add(&pp->arg_fail, &pd->list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Can the command continue if some specified devices were not found?
|
||||
*/
|
||||
if (must_use_all && !dm_list_empty(&pp->arg_fail)) {
|
||||
log_error("Command requires all devices to be found.");
|
||||
return_0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for consistent block sizes.
|
||||
@@ -5211,29 +5171,29 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use process_each_pv to search all existing PVs and devices.
|
||||
*
|
||||
* This is a slightly different way to use process_each_pv, because the
|
||||
* command args (arg_devices) are not being processed directly by
|
||||
* process_each_pv (argc and argv are not passed). Instead,
|
||||
* process_each_pv is processing all existing PVs and devices, and the
|
||||
* single function is matching each of those against the command args
|
||||
* (arg_devices).
|
||||
*
|
||||
* If an arg_devices entry is found during process_each_pv, it's moved
|
||||
* to arg_process if it can be used, or arg_fail if it cannot be used.
|
||||
* If it's added to arg_process but needs a prompt or force option, then
|
||||
* a corresponding prompt entry is added to pp->prompts.
|
||||
*/
|
||||
process_each_pv(cmd, 0, NULL, NULL, 1, PROCESS_SKIP_SCAN,
|
||||
handle, pp->is_remove ? _pvremove_check_single : _pvcreate_check_single);
|
||||
/* check_used moves pd entries into the arg_fail list if pvcreate/pvremove is disallowed */
|
||||
dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) {
|
||||
if (pp->is_remove)
|
||||
_pvremove_check_used(cmd, pp, pd);
|
||||
else
|
||||
_pvcreate_check_used(cmd, pp, pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* A fatal error was found while checking.
|
||||
* If the user specified a uuid for the new PV, check
|
||||
* if a PV on another dev is already using that uuid.
|
||||
*/
|
||||
if (pp->check_failed)
|
||||
goto_bad;
|
||||
if (!pp->is_remove && pp->uuid_str) {
|
||||
struct device *dev;
|
||||
if ((dev = lvmcache_device_from_pvid(cmd, &pp->pva.id, NULL))) {
|
||||
dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) {
|
||||
if (pd->dev != dev) {
|
||||
log_error("UUID %s already in use on \"%s\".", pp->uuid_str, dev_name(dev));
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case: pvremove -ff is allowed to clear a duplicate device in
|
||||
@@ -5246,23 +5206,16 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
!dm_list_empty(&pp->arg_devices) && lvmcache_has_duplicate_devs()) {
|
||||
dm_list_iterate_items_safe(pd, pd2, &pp->arg_devices) {
|
||||
if (lvmcache_dev_is_unused_duplicate(pd->dev)) {
|
||||
log_debug("Found pvremove arg %s: device is a duplicate.", pd->name);
|
||||
log_debug("Check pvremove arg %s device is a duplicate.", dev_name(pd->dev));
|
||||
dm_list_move(&remove_duplicates, &pd->list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if all arg_devices were found by process_each_pv.
|
||||
* Any devices not moved to arg_fail can be processed.
|
||||
*/
|
||||
dm_list_iterate_items(pd, &pp->arg_devices)
|
||||
log_error("Device %s %s.", pd->name, dev_cache_filtered_reason(pd->name));
|
||||
|
||||
/*
|
||||
* Can the command continue if some specified devices were not found?
|
||||
*/
|
||||
if (!dm_list_empty(&pp->arg_devices) && must_use_all)
|
||||
goto_bad;
|
||||
dm_list_splice(&pp->arg_process, &pp->arg_devices);
|
||||
|
||||
/*
|
||||
* Can the command continue if some specified devices cannot be used?
|
||||
@@ -5318,8 +5271,10 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
|
||||
lockf_global(cmd, "un");
|
||||
|
||||
unlocked_for_prompts = 1;
|
||||
|
||||
/*
|
||||
* Process prompts that require asking the user. The orphans lock is
|
||||
* Process prompts that require asking the user. The global lock is
|
||||
* not held, so there's no harm in waiting for a user to respond.
|
||||
*/
|
||||
dm_list_iterate_items_safe(prompt, prompt2, &pp->prompts) {
|
||||
@@ -5350,7 +5305,7 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
|
||||
/*
|
||||
* Reacquire the lock that was released above before waiting, then
|
||||
* check again that the devices can still be used. If the second loop
|
||||
* check again that the devices can still be used. If the second check
|
||||
* finds them changed, or can't find them any more, then they aren't
|
||||
* used. Use a non-blocking request when reacquiring to avoid
|
||||
* potential deadlock since this is not the normal locking sequence.
|
||||
@@ -5361,41 +5316,6 @@ int pvcreate_each_device(struct cmd_context *cmd,
|
||||
goto_out;
|
||||
}
|
||||
|
||||
lvmcache_label_scan(cmd);
|
||||
|
||||
/*
|
||||
* The device args began on the arg_devices list, then the first check
|
||||
* loop moved those entries to arg_process as they were found. Devices
|
||||
* not found during the first loop are not being used, and remain on
|
||||
* arg_devices.
|
||||
*
|
||||
* Now, the arg_process entries are moved to arg_confirm, and the second
|
||||
* check loop moves them back to arg_process as they are found and are
|
||||
* unchanged. Like the first loop, the second loop moves an entry to
|
||||
* arg_fail if it cannot be used. After the second loop, any devices
|
||||
* remaining on arg_confirm were not found and are not used.
|
||||
*/
|
||||
dm_list_splice(&pp->arg_confirm, &pp->arg_process);
|
||||
|
||||
process_each_pv(cmd, 0, NULL, NULL, 1, PROCESS_SKIP_SCAN,
|
||||
handle, _pv_confirm_single);
|
||||
|
||||
dm_list_iterate_items(pd, &pp->arg_confirm)
|
||||
log_error("Device %s %s.", pd->name, dev_cache_filtered_reason(pd->name));
|
||||
|
||||
/* Some devices were not found during the second check. */
|
||||
if (!dm_list_empty(&pp->arg_confirm) && must_use_all)
|
||||
goto_bad;
|
||||
|
||||
/* Some devices changed during the second check. */
|
||||
if (!dm_list_empty(&pp->arg_fail) && must_use_all)
|
||||
goto_bad;
|
||||
|
||||
if (dm_list_empty(&pp->arg_process)) {
|
||||
log_debug("No devices to process.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
do_command:
|
||||
|
||||
dm_list_iterate_items(pd, &pp->arg_process) {
|
||||
@@ -5411,6 +5331,25 @@ do_command:
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the global lock was unlocked to wait for prompts, then
|
||||
* devs could have changed while unlocked, so confirm that
|
||||
* the devs are unchanged since check_used.
|
||||
* Changed pd entries are moved to arg_fail.
|
||||
*/
|
||||
if (unlocked_for_prompts) {
|
||||
dm_list_iterate_items_safe(pd, pd2, &pp->arg_process)
|
||||
_confirm_check_used(cmd, pp, pd);
|
||||
|
||||
if (!dm_list_empty(&pp->arg_fail) && must_use_all)
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
if (dm_list_empty(&pp->arg_process)) {
|
||||
log_debug("No devices to process.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reorder arg_process entries to match the original order of args.
|
||||
*/
|
||||
@@ -5480,6 +5419,10 @@ do_command:
|
||||
log_debug("Using existing orphan PV %s.", pv_dev_name(vgpvl->pv));
|
||||
pvl->pv = vgpvl->pv;
|
||||
dm_list_add(&pp->pvs, &pvl->list);
|
||||
|
||||
device_id_pvcreate(cmd, pd->dev, (const char *)&pvl->pv->id.uuid,
|
||||
arg_str_value(cmd, deviceidtype_ARG, NULL),
|
||||
arg_str_value(cmd, deviceid_ARG, NULL));
|
||||
} else {
|
||||
log_error("Failed to find PV %s", pd->name);
|
||||
dm_list_move(&pp->arg_fail, &pd->list);
|
||||
@@ -5518,6 +5461,10 @@ do_command:
|
||||
continue;
|
||||
}
|
||||
|
||||
device_id_pvcreate(cmd, pd->dev, (const char *)&pv->id.uuid,
|
||||
arg_str_value(cmd, deviceidtype_ARG, NULL),
|
||||
arg_str_value(cmd, deviceid_ARG, NULL));
|
||||
|
||||
log_verbose("Set up physical volume for \"%s\" with %" PRIu64
|
||||
" available sectors.", pv_name, pv_size(pv));
|
||||
|
||||
@@ -5563,6 +5510,8 @@ do_command:
|
||||
continue;
|
||||
}
|
||||
|
||||
device_id_pvremove(cmd, pd->dev);
|
||||
|
||||
log_print_unless_silent("Labels on physical volume \"%s\" successfully wiped.",
|
||||
pd->name);
|
||||
}
|
||||
@@ -5579,10 +5528,15 @@ do_command:
|
||||
|
||||
lvmcache_del_dev_from_duplicates(pd->dev);
|
||||
|
||||
device_id_pvremove(cmd, pd->dev);
|
||||
|
||||
log_print_unless_silent("Labels on physical volume \"%s\" successfully wiped.",
|
||||
pd->name);
|
||||
}
|
||||
|
||||
/* TODO: when vgcreate uses only existing PVs this doesn't change and can be skipped */
|
||||
device_ids_write(cmd);
|
||||
|
||||
/*
|
||||
* Don't keep devs open excl in bcache because the excl will prevent
|
||||
* using that dev elsewhere.
|
||||
|
||||
@@ -991,8 +991,13 @@ static int _vgchange_locktype_single(struct cmd_context *cmd, const char *vg_nam
|
||||
* deactivate it.
|
||||
*/
|
||||
if (vg->lock_type && !strcmp(vg->lock_type, "sanlock") &&
|
||||
(cmd->command->command_enum == vgchange_locktype_CMD))
|
||||
deactivate_lv(cmd, vg->sanlock_lv);
|
||||
(cmd->command->command_enum == vgchange_locktype_CMD)) {
|
||||
if (!deactivate_lv(cmd, vg->sanlock_lv)) {
|
||||
log_error("Failed to deativate %s.",
|
||||
display_lvname(vg->sanlock_lv));
|
||||
return ECMD_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
log_print_unless_silent("Volume group \"%s\" successfully changed", vg->name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user