mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
626242c1bd
We can already detect MD devices internally. But when using MD partitions, these have "block extended major" (blkext) assigned (259). Blkext major is also used in general, so we need to check whether the original device is an MD device actually.
482 lines
11 KiB
C
482 lines
11 KiB
C
/*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <libgen.h> /* dirname, basename */
|
|
#include "lib.h"
|
|
#include "lvm-types.h"
|
|
#include "device.h"
|
|
#include "metadata.h"
|
|
#include "filter.h"
|
|
#include "xlate.h"
|
|
|
|
/* See linux/genhd.h and fs/partitions/msdos */
|
|
|
|
#define PART_MAGIC 0xAA55
|
|
#define PART_MAGIC_OFFSET UINT64_C(0x1FE)
|
|
#define PART_OFFSET UINT64_C(0x1BE)
|
|
|
|
struct partition {
|
|
uint8_t boot_ind;
|
|
uint8_t head;
|
|
uint8_t sector;
|
|
uint8_t cyl;
|
|
uint8_t sys_ind; /* partition type */
|
|
uint8_t end_head;
|
|
uint8_t end_sector;
|
|
uint8_t end_cyl;
|
|
uint32_t start_sect;
|
|
uint32_t nr_sects;
|
|
} __attribute__((packed));
|
|
|
|
static int _is_partitionable(struct device *dev)
|
|
{
|
|
int parts = max_partitions(MAJOR(dev->dev));
|
|
|
|
/* All MD devices are partitionable via blkext (as of 2.6.28) */
|
|
if (MAJOR(dev->dev) == md_major())
|
|
return 1;
|
|
|
|
if ((parts <= 1) || (MINOR(dev->dev) % parts))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _has_partition_table(struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
unsigned p;
|
|
uint16_t buf[SECTOR_SIZE/sizeof(uint16_t)];
|
|
uint16_t *part_magic;
|
|
struct partition *part;
|
|
|
|
if (!dev_read(dev, UINT64_C(0), sizeof(buf), &buf))
|
|
return_0;
|
|
|
|
/* FIXME Check for other types of partition table too */
|
|
|
|
/* Check for msdos partition table */
|
|
part_magic = buf + PART_MAGIC_OFFSET/sizeof(buf[0]);
|
|
if ((*part_magic == xlate16(PART_MAGIC))) {
|
|
part = (struct partition *) (buf + PART_OFFSET/sizeof(buf[0]));
|
|
for (p = 0; p < 4; p++, part++) {
|
|
/* Table is invalid if boot indicator not 0 or 0x80 */
|
|
if ((part->boot_ind & 0x7f)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
/* Must have at least one non-empty partition */
|
|
if (part->nr_sects)
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int is_partitioned_dev(struct device *dev)
|
|
{
|
|
if (!_is_partitionable(dev))
|
|
return 0;
|
|
|
|
return _has_partition_table(dev);
|
|
}
|
|
|
|
#if 0
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/major.h>
|
|
#include <linux/genhd.h>
|
|
|
|
int _get_partition_type(struct dev_filter *filter, struct device *d);
|
|
|
|
#define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
|
|
|
|
int is_extended_partition(struct device *d)
|
|
{
|
|
return (MINOR_PART(d) > 4) ? 1 : 0;
|
|
}
|
|
|
|
struct device *dev_primary(struct dev_mgr *dm, struct device *d)
|
|
{
|
|
struct device *ret;
|
|
|
|
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
|
/* FIXME: Needs replacing with a 'refresh' */
|
|
if (!ret) {
|
|
init_dev_scan(dm);
|
|
ret = dev_by_dev(dm, d->dev - MINOR_PART(dm, d));
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int partition_type_is_lvm(struct dev_mgr *dm, struct device *d)
|
|
{
|
|
int pt;
|
|
|
|
pt = _get_partition_type(dm, d);
|
|
|
|
if (!pt) {
|
|
if (is_whole_disk(dm, d))
|
|
/* FIXME: Overloaded pt=0 in error cases */
|
|
return 1;
|
|
else {
|
|
log_error
|
|
("%s: missing partition table "
|
|
"on partitioned device", d->name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (is_whole_disk(dm, d)) {
|
|
log_error("%s: looks to possess partition table", d->name);
|
|
return 0;
|
|
}
|
|
|
|
/* check part type */
|
|
if (pt != LVM_PARTITION && pt != LVM_NEW_PARTITION) {
|
|
log_error("%s: invalid partition type 0x%x "
|
|
"(must be 0x%x)", d->name, pt, LVM_NEW_PARTITION);
|
|
return 0;
|
|
}
|
|
|
|
if (pt == LVM_PARTITION) {
|
|
log_error
|
|
("%s: old LVM partition type found - please change to 0x%x",
|
|
d->name, LVM_NEW_PARTITION);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int _get_partition_type(struct dev_mgr *dm, struct device *d)
|
|
{
|
|
int pv_handle = -1;
|
|
struct device *primary;
|
|
ssize_t read_ret;
|
|
ssize_t bytes_read = 0;
|
|
char *buffer;
|
|
unsigned short *s_buffer;
|
|
struct partition *part;
|
|
loff_t offset = 0;
|
|
loff_t extended_offset = 0;
|
|
int part_sought;
|
|
int part_found = 0;
|
|
int first_partition = 1;
|
|
int extended_partition = 0;
|
|
int p;
|
|
|
|
if (!(primary = dev_primary(dm, d))) {
|
|
log_error
|
|
("Failed to find main device containing partition %s",
|
|
d->name);
|
|
return 0;
|
|
}
|
|
|
|
if (!(buffer = dm_malloc(SECTOR_SIZE))) {
|
|
log_error("Failed to allocate partition table buffer");
|
|
return 0;
|
|
}
|
|
|
|
/* Get partition table */
|
|
if ((pv_handle = open(primary->name, O_RDONLY)) < 0) {
|
|
log_error("%s: open failed: %s", primary->name,
|
|
strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
s_buffer = (unsigned short *) buffer;
|
|
part = (struct partition *) (buffer + 0x1be);
|
|
part_sought = MINOR_PART(dm, d);
|
|
|
|
do {
|
|
bytes_read = 0;
|
|
|
|
if (llseek(pv_handle, offset * SECTOR_SIZE, SEEK_SET) == -1) {
|
|
log_error("%s: llseek failed: %s",
|
|
primary->name, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
while ((bytes_read < SECTOR_SIZE) &&
|
|
(read_ret =
|
|
read(pv_handle, buffer + bytes_read,
|
|
SECTOR_SIZE - bytes_read)) != -1)
|
|
bytes_read += read_ret;
|
|
|
|
if (read_ret == -1) {
|
|
log_error("%s: read failed: %s", primary->name,
|
|
strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (s_buffer[255] == 0xAA55) {
|
|
if (is_whole_disk(dm, d))
|
|
return -1;
|
|
} else
|
|
return 0;
|
|
|
|
extended_partition = 0;
|
|
|
|
/* Loop through primary partitions */
|
|
for (p = 0; p < 4; p++) {
|
|
if (part[p].sys_ind == DOS_EXTENDED_PARTITION ||
|
|
part[p].sys_ind == LINUX_EXTENDED_PARTITION
|
|
|| part[p].sys_ind == WIN98_EXTENDED_PARTITION) {
|
|
extended_partition = 1;
|
|
offset = extended_offset + part[p].start_sect;
|
|
if (extended_offset == 0)
|
|
extended_offset = part[p].start_sect;
|
|
if (first_partition == 1)
|
|
part_found++;
|
|
} else if (first_partition == 1) {
|
|
if (p == part_sought) {
|
|
if (part[p].sys_ind == 0) {
|
|
/* missing primary? */
|
|
return 0;
|
|
}
|
|
} else
|
|
part_found++;
|
|
} else if (!part[p].sys_ind)
|
|
part_found++;
|
|
|
|
if (part_sought == part_found)
|
|
return part[p].sys_ind;
|
|
|
|
}
|
|
first_partition = 0;
|
|
}
|
|
while (extended_partition == 1);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef linux
|
|
|
|
int get_primary_dev(const char *sysfs_dir,
|
|
const struct device *dev, dev_t *result)
|
|
{
|
|
char path[PATH_MAX+1];
|
|
char temp_path[PATH_MAX+1];
|
|
char buffer[64];
|
|
struct stat info;
|
|
FILE *fp;
|
|
uint32_t pri_maj, pri_min;
|
|
int ret = 0;
|
|
|
|
/* check if dev is a partition */
|
|
if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/partition",
|
|
sysfs_dir, (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
|
|
log_error("dm_snprintf partition failed");
|
|
return ret;
|
|
}
|
|
|
|
if (stat(path, &info) == -1) {
|
|
if (errno != ENOENT)
|
|
log_sys_error("stat", path);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* extract parent's path from the partition's symlink, e.g.:
|
|
* - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1
|
|
* - dirname ../../block/md0/md0p1 = ../../block/md0
|
|
* - basename ../../block/md0/md0 = md0
|
|
* Parent's 'dev' sysfs attribute = /sys/block/md0/dev
|
|
*/
|
|
if (readlink(dirname(path), temp_path, PATH_MAX) < 0) {
|
|
log_sys_error("readlink", path);
|
|
return ret;
|
|
}
|
|
|
|
if (dm_snprintf(path, PATH_MAX, "%s/block/%s/dev",
|
|
sysfs_dir, basename(dirname(temp_path))) < 0) {
|
|
log_error("dm_snprintf dev failed");
|
|
return ret;
|
|
}
|
|
|
|
/* finally, parse 'dev' attribute and create corresponding dev_t */
|
|
if (stat(path, &info) == -1) {
|
|
if (errno == ENOENT)
|
|
log_error("sysfs file %s does not exist", path);
|
|
else
|
|
log_sys_error("stat", path);
|
|
return ret;
|
|
}
|
|
|
|
fp = fopen(path, "r");
|
|
if (!fp) {
|
|
log_sys_error("fopen", path);
|
|
return ret;
|
|
}
|
|
|
|
if (!fgets(buffer, sizeof(buffer), fp)) {
|
|
log_sys_error("fgets", path);
|
|
goto out;
|
|
}
|
|
|
|
if (sscanf(buffer, "%d:%d", &pri_maj, &pri_min) != 2) {
|
|
log_error("sysfs file %s not in expected MAJ:MIN format: %s",
|
|
path, buffer);
|
|
goto out;
|
|
}
|
|
*result = MKDEV(pri_maj, pri_min);
|
|
ret = 1;
|
|
|
|
out:
|
|
if (fclose(fp))
|
|
log_sys_error("fclose", path);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned long _dev_topology_attribute(const char *attribute,
|
|
const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
const char *sysfs_fmt_str = "%s/dev/block/%d:%d/%s";
|
|
char path[PATH_MAX+1], buffer[64];
|
|
FILE *fp;
|
|
struct stat info;
|
|
dev_t uninitialized_var(primary);
|
|
unsigned long result = 0UL;
|
|
|
|
if (!attribute || !*attribute)
|
|
return_0;
|
|
|
|
if (!sysfs_dir || !*sysfs_dir)
|
|
return_0;
|
|
|
|
if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
|
|
(int)MAJOR(dev->dev), (int)MINOR(dev->dev),
|
|
attribute) < 0) {
|
|
log_error("dm_snprintf %s failed", attribute);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* check if the desired sysfs attribute exists
|
|
* - if not: either the kernel doesn't have topology support
|
|
* or the device could be a partition
|
|
*/
|
|
if (stat(path, &info) == -1) {
|
|
if (errno != ENOENT) {
|
|
log_sys_error("stat", path);
|
|
return 0;
|
|
}
|
|
if (!get_primary_dev(sysfs_dir, dev, &primary))
|
|
return 0;
|
|
|
|
/* get attribute from partition's primary device */
|
|
if (dm_snprintf(path, PATH_MAX, sysfs_fmt_str, sysfs_dir,
|
|
(int)MAJOR(primary), (int)MINOR(primary),
|
|
attribute) < 0) {
|
|
log_error("primary dm_snprintf %s failed", attribute);
|
|
return 0;
|
|
}
|
|
if (stat(path, &info) == -1) {
|
|
if (errno != ENOENT)
|
|
log_sys_error("stat", path);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!(fp = fopen(path, "r"))) {
|
|
log_sys_error("fopen", path);
|
|
return 0;
|
|
}
|
|
|
|
if (!fgets(buffer, sizeof(buffer), fp)) {
|
|
log_sys_error("fgets", path);
|
|
goto out;
|
|
}
|
|
|
|
if (sscanf(buffer, "%lu", &result) != 1) {
|
|
log_error("sysfs file %s not in expected format: %s", path,
|
|
buffer);
|
|
goto out;
|
|
}
|
|
|
|
log_very_verbose("Device %s %s is %lu bytes.",
|
|
dev_name(dev), attribute, result);
|
|
|
|
out:
|
|
if (fclose(fp))
|
|
log_sys_error("fclose", path);
|
|
|
|
return result >> SECTOR_SHIFT;
|
|
}
|
|
|
|
unsigned long dev_alignment_offset(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return _dev_topology_attribute("alignment_offset",
|
|
sysfs_dir, dev);
|
|
}
|
|
|
|
unsigned long dev_minimum_io_size(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return _dev_topology_attribute("queue/minimum_io_size",
|
|
sysfs_dir, dev);
|
|
}
|
|
|
|
unsigned long dev_optimal_io_size(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return _dev_topology_attribute("queue/optimal_io_size",
|
|
sysfs_dir, dev);
|
|
}
|
|
|
|
#else
|
|
|
|
int get_primary_dev(const char *sysfs_dir,
|
|
struct device *dev, dev_t *result)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
unsigned long dev_alignment_offset(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return 0UL;
|
|
}
|
|
|
|
unsigned long dev_minimum_io_size(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return 0UL;
|
|
}
|
|
|
|
unsigned long dev_optimal_io_size(const char *sysfs_dir,
|
|
struct device *dev)
|
|
{
|
|
return 0UL;
|
|
}
|
|
|
|
#endif
|