1
0
mirror of git://sourceware.org/git/lvm2.git synced 2026-02-10 08:32:46 +03:00

Compare commits

..

20 Commits

Author SHA1 Message Date
David Teigland
6029808eac device usage based on devices_file and device_id
devices_file, e.g. /etc/lvm/lvm_devices.dat,
is a list of devices that lvm can use.
Option --devicesfile can sepecify a different file
with a separate set of devices for lvm to use.
This option allows different applications to use
lvm on different sets of devices.

In most cases (with limited exceptions), lvm will not
read or use a device not listed in devices_file.
When the devices_file is used, the filter-regex is
not used and the filter settings in lvm.conf are
ignored.  lvm uses the devices_file to control access
to devices using the new filter-deviceid.

Setting --devicesfile to "" on the command line, or
devicesfile to "" in lvm.conf will disable the use
of the devices_file and filter-deviceid.  This
allows lvm to see and use any device on the system.
In this case lvm will fall back to using filter-regex
and the filter config settings in lvm.conf.

device_id, e.g. wwid or serial number from sysfs,
is a unique ID that identifies a device without
reading it, and which will change if the device
is copied to another.  The device_id is used in the
devices_file and is included in VG metadata sections.

Each device_id has a device_id_type which indicates
where the device_id comes from, e.g. "sys_wwid"
means the device_id comes from the sysfs wwid file.
Others are sys_serial, mpath_uuid, loop_file, devname.
(devname is the device path which is a fallback when
no other proper device_id_type is available.)

filter-deviceid permits lvm to use only devices
on the system that have a device_id matching a
devices_file entry.  Using the device_id, lvm can
determine the set of devices to use without reading
any devices, so devices_file will constrain lvm in
two ways:
1. it limits the devices that lvm will read.
2. it limits the devices that lvm will use.

In some uncommon cases, e.g. when devices have no
unique ID and device_id has to fall back to using
the devname, lvm may need to read all devices on the
system to determine which ones correspond to the
devices_file entries.  In this case, the devices_file
does not limit the devices that lvm reads, but it does
limit the devices that lvm uses.

pvcreate/vgcreate/vgextend are not constrained by
the devices_file, and will look outside it to find
the new PV.  They assign the new PV a device_id
and add it to the devices_file.  It would be possible
to explicitly add new PVs to devices_file before using
them in pvcreate/etc, in which case these commands
would not need to access devices outside devices_file.
(A config setting may be added to control the ability
of these commands to search outside devices_list.)

TODO:
vgimportdevices [--all, --deviceidupdate] VG
pvchange --deviceidupdate PV
vgchange --deviceidupdate VG
pvs -o deviceidtype,deviceid
duplicate PV resolution using device_id
vgimportclone
config setting device_id_types to control idtypes used
lvmdevices command to manage devices_file
search for pvid when no wwid is available and PV is missing
md device_id_type
config setting pvcreate_extends_devices_file=0|1
shortsystemid crc of systemid and written in pv header
use shortsystemid for new filter and orphan PV ownership

TODO: new lvmdevices command:

--add <devname>
Adds devices_file entry, reads device header.

--del <devname>
Removes devices_file entry.

--addpvid <pvid>
Reads pv header of all devices to find <pvid>,
if found adds devices_file entry.

--delpvid <pvid>
Removes devices_file entry.

--adddeviceid <device_id> --deviceidtype <idtype>
Reads <idtype> of all devices (e.g. from sysfs) to find a match,
if found, reads pv header and adds devices_file entry.

--deldeviceid <device_id>
Removes devices_file entry.
2020-07-01 13:08:38 -05:00
David Teigland
b078703302 pvcreate/pvremove: reimplement checks 2020-06-23 13:19:11 -05:00
David Teigland
fa9eb76a5d improve info about vgck updatemetadata
Add man page info about this option, and add log messages
pointing to this option.
2020-06-03 12:38:27 -05:00
Zhao Heming
b59127a838 Change dev->bcache_fd default value from 0 to -1
This fix can avoid bcache_fd will mistakenly open/close in later.

Signed-off-by: Zhao Heming <heming.zhao@suse.com>
2020-06-01 12:22:15 -05:00
David Teigland
d14a8040d4 Revert "pvck: dump headers_only to skip metadata text"
This reverts commit 5410dd5441.

Accidental push.
2020-05-29 13:26:43 -05:00
David Teigland
ae029fcced integrity: skip calling add when removing images
When lvconvert is used to remove raid images, we can
skip calling lv_add_integrity_to_raid(), which finds
nothing to do, but the the blocksize validation would
be called unnecessarily and trigger spurious errors.
2020-05-29 13:18:24 -05:00
David Teigland
7b04ed07ba tests: integrity wait for sync
The test was using a raid+integrity LV without
first waiting for the integrity sync, which could
cause the test to fail (depending on init speed)
where it depends on integrity to work in uninitialized
areas.

Also use cmp instead of diff.
2020-05-29 10:57:56 -05:00
David Teigland
5410dd5441 pvck: dump headers_only to skip metadata text
pvck --dump headers reads the metadata text area
to compute the text metadata checksum to compare
with the mda_header checksum.
The new header_only will skip reading the metadata
text and not validate the mda_header checksum.
2020-05-28 15:51:59 -05:00
Marian Csontos
be61bd6ff5 test: Warn and exit on problematic integrity device behavior
The first leg of integrity enabled raid device sometimes does not get
recalculated.
2020-05-28 17:04:35 +02:00
David Teigland
74a211cfd3 lvconvert: error when using existing cachevol
Check if LV is an existing cachevol before attempting
to use it again as a cachevol or cachepool.
2020-05-22 14:12:34 -05:00
Zdenek Kabelac
bb41ca86fa tests: also udev wait on clean-up path 2020-05-21 16:03:41 +02:00
Marian Csontos
53803821de test: Use printf to generate data
...to avoid unnecessary dependency on python
2020-05-21 15:33:24 +02:00
Marian Csontos
b5811b7c9c tests: Use python single liner to generate data 2020-05-21 15:11:22 +02:00
Marian Csontos
70a45c44e8 build: make generate 2020-05-21 15:02:31 +02:00
Zdenek Kabelac
d3b515cea5 tests: add wait on udev processing
Trying to avoid collision with udev watch rule preventing to
succeed 'dmsetup remove' becuase it keeps device open.
2020-05-20 16:01:20 +02:00
Zdenek Kabelac
deb5160181 list: use container_of
Reuse macro
2020-05-20 16:01:20 +02:00
Zdenek Kabelac
16da6651a1 pvck: set dump on one call
arg_str_value() has built-in  arg_is_set().

Also this makes it obvious to coverity 'dump != NULL' & 'repair != NULL'
at the branch code path.
2020-05-20 15:55:39 +02:00
Zdenek Kabelac
cf74123830 cov: lvconvert: missing check for function failure 2020-05-20 15:55:39 +02:00
Zdenek Kabelac
ce8277b47e cov: check strdup for NULL 2020-05-20 15:55:39 +02:00
Zdenek Kabelac
33fdeaf3f1 cov: check for deactivation failure 2020-05-20 15:55:39 +02:00
41 changed files with 1763 additions and 460 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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 \

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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.

View File

@@ -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"

View File

@@ -320,4 +320,6 @@
#define DEFAULT_MD_COMPONENT_CHECKS "auto"
#define DEFAULT_DEVICES_FILE "/etc/lvm/lvm_devices.dat"
#endif /* _LVM_DEFAULTS_H */

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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
View 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
View 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

View 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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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);

View File

@@ -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") &&

View File

@@ -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.

View File

@@ -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);