1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/filters/filter.c

355 lines
8.2 KiB
C

/*
* 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., 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>
#include "device/device-types.h"
#define NUMBER_OF_MAJORS 4096
#define PARTITION_SCSI_DEVICE (1 << 0)
static struct {
int max_partitions; /* 0 means LVM won't use this major number. */
int flags;
} _partitions[NUMBER_OF_MAJORS];
static int _md_major = -1;
static int _blkext_major = -1;
static int _drbd_major = -1;
static int _device_mapper_major = -1;
static int _emcpower_major = -1;
static int _power2_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) == _md_major)
return 1;
if (MAJOR(dev->dev) == _drbd_major)
return 1;
if (MAJOR(dev->dev) == _emcpower_major)
return 1;
if (MAJOR(dev->dev) == _power2_major)
return 1;
if ((MAJOR(dev->dev) == _blkext_major) &&
(get_primary_dev(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) == _emcpower_major)
return "EMCPOWER";
if (MAJOR(dev->dev) == _power2_major)
return "POWER2";
if (MAJOR(dev->dev) == _blkext_major)
return "BLKEXT";
return "";
}
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 (!_partitions[MAJOR(dev->dev)].max_partitions) {
log_debug_devs("%s: Skipping: Unrecognised LVM device type %"
PRIu64, name, (uint64_t) MAJOR(dev->dev));
return 0;
}
/* Check it's accessible */
if (!dev_open_readonly_quiet(dev)) {
log_debug_devs("%s: Skipping: open failed", name);
return 0;
}
/* Check it's not too small */
if (!dev_get_size(dev, &size)) {
log_debug_devs("%s: Skipping: dev_get_size failed", name);
goto out;
}
if (size < pv_min_size()) {
log_debug_devs("%s: Skipping: Too small to hold a PV", name);
goto out;
}
if (is_partitioned_dev(dev)) {
log_debug_devs("%s: Skipping: Partition table signature found",
name);
goto out;
}
ret = 1;
out:
if (!dev_close(dev))
stack;
return ret;
}
static int _scan_proc_dev(const char *proc, const struct dm_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 dm_config_value *cv;
const char *name;
char *nl;
if (!*proc) {
log_verbose("No proc filesystem found: using all block device "
"types");
for (i = 0; i < NUMBER_OF_MAJORS; i++)
_partitions[i].max_partitions = 1;
return 1;
}
/* All types unrecognised initially */
memset(_partitions, 0, sizeof(_partitions));
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, sizeof(line), pd) != NULL) {
i = 0;
while (line[i] == ' ')
i++;
/* If it's not a number it may be name of section */
line_maj = atoi(((char *) (line + i)));
if (line_maj < 0 || line_maj >= NUMBER_OF_MAJORS) {
/*
* Device numbers shown in /proc/devices are actually direct
* numbers passed to registering function, however the kernel
* uses only 12 bits, so use just 12 bits for major.
*/
if ((nl = strchr(line, '\n'))) *nl = '\0';
log_warn("WARNING: /proc/devices line: %s, replacing major with %d.",
line, line_maj & (NUMBER_OF_MAJORS - 1));
line_maj &= (NUMBER_OF_MAJORS - 1);
}
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] == ' ')
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 EMC powerpath */
if (!strncmp("emcpower", line + i, 8) && isspace(*(line + i + 8)))
_emcpower_major = line_maj;
if (!strncmp("power2", line + i, 6) && isspace(*(line + i + 6)))
_power2_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;
/* Major is SCSI device */
if (!strncmp("sd", line + i, 2) && isspace(*(line + i + 2)))
_partitions[line_maj].flags |= PARTITION_SCSI_DEVICE;
/* Go through the valid device names and if there is a
match store max number of partitions */
for (j = 0; _device_info[j].name[0]; 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)) {
_partitions[line_maj].max_partitions =
_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 != DM_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 != DM_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)) {
_partitions[line_maj].max_partitions = cv->v.i;
break;
}
}
}
if (fclose(pd))
log_sys_error("fclose", proc_devices);
return 1;
}
int max_partitions(int major)
{
if (major >= NUMBER_OF_MAJORS)
return 0;
return _partitions[major].max_partitions;
}
int major_is_scsi_device(int major)
{
if (major >= NUMBER_OF_MAJORS)
return 0;
return (_partitions[major].flags & PARTITION_SCSI_DEVICE) ? 1 : 0;
}
static 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);
}
struct dev_filter *lvm_type_filter_create(const char *proc,
const struct dm_config_node *cn)
{
struct dev_filter *f;
if (!(f = dm_zalloc(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;
}