mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
794e94fe16
Add configurable option to define minimal size of of block device usable as a PV. pv_min_size() is added to lvm-globals and it's being initialized through _process_config. Macro PV_MIN_SIZE is unused and removed. New define DEFAULT_PV_MIN_SIZE_KB is added to lvm-global and unlike PV_MIN_SIZE it uses KB units. Should help users with various slow devices attached to the system, which cannot be easily filtered out (like FDD on /dev/sdX): https://bugzilla.redhat.com/show_bug.cgi?id=644578
348 lines
8.4 KiB
C
348 lines
8.4 KiB
C
/*
|
|
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "lib.h"
|
|
#include "dev-cache.h"
|
|
#include "filter.h"
|
|
#include "lvm-string.h"
|
|
#include "config.h"
|
|
#include "metadata.h"
|
|
#include "activate.h"
|
|
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
|
|
#define NUMBER_OF_MAJORS 4096
|
|
|
|
/* 0 means LVM won't use this major number. */
|
|
static int _max_partitions_by_major[NUMBER_OF_MAJORS];
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
const int max_partitions;
|
|
} device_info_t;
|
|
|
|
static int _md_major = -1;
|
|
static int _blkext_major = -1;
|
|
static int _drbd_major = -1;
|
|
static int _device_mapper_major = -1;
|
|
|
|
int dm_major(void)
|
|
{
|
|
return _device_mapper_major;
|
|
}
|
|
|
|
int md_major(void)
|
|
{
|
|
return _md_major;
|
|
}
|
|
|
|
int blkext_major(void)
|
|
{
|
|
return _blkext_major;
|
|
}
|
|
|
|
int dev_subsystem_part_major(const struct device *dev)
|
|
{
|
|
dev_t primary_dev;
|
|
|
|
if (MAJOR(dev->dev) == -1)
|
|
return 0;
|
|
|
|
if (MAJOR(dev->dev) == _md_major)
|
|
return 1;
|
|
|
|
if (MAJOR(dev->dev) == _drbd_major)
|
|
return 1;
|
|
|
|
if ((MAJOR(dev->dev) == _blkext_major) &&
|
|
(get_primary_dev(sysfs_dir_path(), dev, &primary_dev)) &&
|
|
(MAJOR(primary_dev) == _md_major))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *dev_subsystem_name(const struct device *dev)
|
|
{
|
|
if (MAJOR(dev->dev) == _md_major)
|
|
return "MD";
|
|
|
|
if (MAJOR(dev->dev) == _drbd_major)
|
|
return "DRBD";
|
|
|
|
if (MAJOR(dev->dev) == _blkext_major)
|
|
return "BLKEXT";
|
|
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
* Devices are only checked for partition tables if their minor number
|
|
* is a multiple of the number corresponding to their type below
|
|
* i.e. this gives the granularity of whole-device minor numbers.
|
|
* Use 1 if the device is not partitionable.
|
|
*
|
|
* The list can be supplemented with devices/types in the config file.
|
|
*/
|
|
static const device_info_t device_info[] = {
|
|
{"ide", 64}, /* IDE disk */
|
|
{"sd", 16}, /* SCSI disk */
|
|
{"md", 1}, /* Multiple Disk driver (SoftRAID) */
|
|
{"mdp", 1}, /* Partitionable MD */
|
|
{"loop", 1}, /* Loop device */
|
|
{"dasd", 4}, /* DASD disk (IBM S/390, zSeries) */
|
|
{"dac960", 8}, /* DAC960 */
|
|
{"nbd", 16}, /* Network Block Device */
|
|
{"ida", 16}, /* Compaq SMART2 */
|
|
{"cciss", 16}, /* Compaq CCISS array */
|
|
{"ubd", 16}, /* User-mode virtual block device */
|
|
{"ataraid", 16}, /* ATA Raid */
|
|
{"drbd", 16}, /* Distributed Replicated Block Device */
|
|
{"emcpower", 16}, /* EMC Powerpath */
|
|
{"power2", 16}, /* EMC Powerpath */
|
|
{"i2o_block", 16}, /* i2o Block Disk */
|
|
{"iseries/vd", 8}, /* iSeries disks */
|
|
{"gnbd", 1}, /* Network block device */
|
|
{"ramdisk", 1}, /* RAM disk */
|
|
{"aoe", 16}, /* ATA over Ethernet */
|
|
{"device-mapper", 1}, /* Other mapped devices */
|
|
{"xvd", 16}, /* Xen virtual block device */
|
|
{"vdisk", 8}, /* SUN's LDOM virtual block device */
|
|
{"ps3disk", 16}, /* PlayStation 3 internal disk */
|
|
{"virtblk", 8}, /* VirtIO disk */
|
|
{"mmc", 16}, /* MMC block device */
|
|
{"blkext", 1}, /* Extended device partitions */
|
|
{"fio", 16}, /* Fusion */
|
|
{NULL, 0}
|
|
};
|
|
|
|
static int _passes_lvm_type_device_filter(struct dev_filter *f __attribute__((unused)),
|
|
struct device *dev)
|
|
{
|
|
const char *name = dev_name(dev);
|
|
int ret = 0;
|
|
uint64_t size;
|
|
|
|
/* Is this a recognised device type? */
|
|
if (!_max_partitions_by_major[MAJOR(dev->dev)]) {
|
|
log_debug("%s: Skipping: Unrecognised LVM device type %"
|
|
PRIu64, name, (uint64_t) MAJOR(dev->dev));
|
|
return 0;
|
|
}
|
|
|
|
/* Check it's accessible */
|
|
if (!dev_open_flags(dev, O_RDONLY, 1, 1)) {
|
|
log_debug("%s: Skipping: open failed", name);
|
|
return 0;
|
|
}
|
|
|
|
/* Check it's not too small */
|
|
if (!dev_get_size(dev, &size)) {
|
|
log_debug("%s: Skipping: dev_get_size failed", name);
|
|
goto out;
|
|
}
|
|
|
|
if (size < pv_min_size()) {
|
|
log_debug("%s: Skipping: Too small to hold a PV", name);
|
|
goto out;
|
|
}
|
|
|
|
if (is_partitioned_dev(dev)) {
|
|
log_debug("%s: Skipping: Partition table signature found",
|
|
name);
|
|
goto out;
|
|
}
|
|
|
|
ret = 1;
|
|
|
|
out:
|
|
dev_close(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int _scan_proc_dev(const char *proc, const struct config_node *cn)
|
|
{
|
|
char line[80];
|
|
char proc_devices[PATH_MAX];
|
|
FILE *pd = NULL;
|
|
int i, j = 0;
|
|
int line_maj = 0;
|
|
int blocksection = 0;
|
|
size_t dev_len = 0;
|
|
const struct config_value *cv;
|
|
const char *name;
|
|
|
|
|
|
if (!*proc) {
|
|
log_verbose("No proc filesystem found: using all block device "
|
|
"types");
|
|
for (i = 0; i < NUMBER_OF_MAJORS; i++)
|
|
_max_partitions_by_major[i] = 1;
|
|
return 1;
|
|
}
|
|
|
|
/* All types unrecognised initially */
|
|
memset(_max_partitions_by_major, 0, sizeof(int) * NUMBER_OF_MAJORS);
|
|
|
|
if (dm_snprintf(proc_devices, sizeof(proc_devices),
|
|
"%s/devices", proc) < 0) {
|
|
log_error("Failed to create /proc/devices string");
|
|
return 0;
|
|
}
|
|
|
|
if (!(pd = fopen(proc_devices, "r"))) {
|
|
log_sys_error("fopen", proc_devices);
|
|
return 0;
|
|
}
|
|
|
|
while (fgets(line, 80, pd) != NULL) {
|
|
i = 0;
|
|
while (line[i] == ' ' && line[i] != '\0')
|
|
i++;
|
|
|
|
/* If it's not a number it may be name of section */
|
|
line_maj = atoi(((char *) (line + i)));
|
|
if (!line_maj) {
|
|
blocksection = (line[i] == 'B') ? 1 : 0;
|
|
continue;
|
|
}
|
|
|
|
/* We only want block devices ... */
|
|
if (!blocksection)
|
|
continue;
|
|
|
|
/* Find the start of the device major name */
|
|
while (line[i] != ' ' && line[i] != '\0')
|
|
i++;
|
|
while (line[i] == ' ' && line[i] != '\0')
|
|
i++;
|
|
|
|
/* Look for md device */
|
|
if (!strncmp("md", line + i, 2) && isspace(*(line + i + 2)))
|
|
_md_major = line_maj;
|
|
|
|
/* Look for blkext device */
|
|
if (!strncmp("blkext", line + i, 6) && isspace(*(line + i + 6)))
|
|
_blkext_major = line_maj;
|
|
|
|
/* Look for drbd device */
|
|
if (!strncmp("drbd", line + i, 4) && isspace(*(line + i + 4)))
|
|
_drbd_major = line_maj;
|
|
|
|
/* Look for device-mapper device */
|
|
/* FIXME Cope with multiple majors */
|
|
if (!strncmp("device-mapper", line + i, 13) && isspace(*(line + i + 13)))
|
|
_device_mapper_major = line_maj;
|
|
|
|
/* Go through the valid device names and if there is a
|
|
match store max number of partitions */
|
|
for (j = 0; device_info[j].name != NULL; j++) {
|
|
dev_len = strlen(device_info[j].name);
|
|
if (dev_len <= strlen(line + i) &&
|
|
!strncmp(device_info[j].name, line + i, dev_len) &&
|
|
(line_maj < NUMBER_OF_MAJORS)) {
|
|
_max_partitions_by_major[line_maj] =
|
|
device_info[j].max_partitions;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!cn)
|
|
continue;
|
|
|
|
/* Check devices/types for local variations */
|
|
for (cv = cn->v; cv; cv = cv->next) {
|
|
if (cv->type != CFG_STRING) {
|
|
log_error("Expecting string in devices/types "
|
|
"in config file");
|
|
if (fclose(pd))
|
|
log_sys_error("fclose", proc_devices);
|
|
return 0;
|
|
}
|
|
dev_len = strlen(cv->v.str);
|
|
name = cv->v.str;
|
|
cv = cv->next;
|
|
if (!cv || cv->type != CFG_INT) {
|
|
log_error("Max partition count missing for %s "
|
|
"in devices/types in config file",
|
|
name);
|
|
if (fclose(pd))
|
|
log_sys_error("fclose", proc_devices);
|
|
return 0;
|
|
}
|
|
if (!cv->v.i) {
|
|
log_error("Zero partition count invalid for "
|
|
"%s in devices/types in config file",
|
|
name);
|
|
if (fclose(pd))
|
|
log_sys_error("fclose", proc_devices);
|
|
return 0;
|
|
}
|
|
if (dev_len <= strlen(line + i) &&
|
|
!strncmp(name, line + i, dev_len) &&
|
|
(line_maj < NUMBER_OF_MAJORS)) {
|
|
_max_partitions_by_major[line_maj] = cv->v.i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fclose(pd))
|
|
log_sys_error("fclose", proc_devices);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int max_partitions(int major)
|
|
{
|
|
return _max_partitions_by_major[major];
|
|
}
|
|
|
|
struct dev_filter *lvm_type_filter_create(const char *proc,
|
|
const struct config_node *cn)
|
|
{
|
|
struct dev_filter *f;
|
|
|
|
if (!(f = dm_malloc(sizeof(struct dev_filter)))) {
|
|
log_error("LVM type filter allocation failed");
|
|
return NULL;
|
|
}
|
|
|
|
f->passes_filter = _passes_lvm_type_device_filter;
|
|
f->destroy = lvm_type_filter_destroy;
|
|
f->use_count = 0;
|
|
f->private = NULL;
|
|
|
|
if (!_scan_proc_dev(proc, cn)) {
|
|
dm_free(f);
|
|
return_NULL;
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
void lvm_type_filter_destroy(struct dev_filter *f)
|
|
{
|
|
if (f->use_count)
|
|
log_error(INTERNAL_ERROR "Destroying lvm_type filter while in use %u times.", f->use_count);
|
|
|
|
dm_free(f);
|
|
}
|