2001-09-25 16:49:28 +04:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2001 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2001-09-25 16:49:28 +04:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
2001-09-25 16:49:28 +04:00
*
2004-03-30 23:35:44 +04:00
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2001-09-25 16:49:28 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-09-25 16:49:28 +04:00
*/
2009-08-01 21:09:48 +04:00
# include <libgen.h> /* dirname, basename */
2004-11-23 14:44:04 +03:00
# include "lib.h"
# include "lvm-types.h"
# include "device.h"
# include "metadata.h"
# include "filter.h"
2004-11-28 01:07:41 +03:00
# include "xlate.h"
2004-11-23 14:44:04 +03:00
2004-12-21 19:10:25 +03:00
/* See linux/genhd.h and fs/partitions/msdos */
2004-11-23 14:44:04 +03:00
# define PART_MAGIC 0xAA55
2004-12-21 19:10:25 +03:00
# define PART_MAGIC_OFFSET UINT64_C(0x1FE)
# define PART_OFFSET UINT64_C(0x1BE)
struct partition {
2008-01-30 17:00:02 +03:00
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 ;
2004-12-21 19:10:25 +03:00
} __attribute__ ( ( packed ) ) ;
2004-11-23 14:44:04 +03:00
2004-11-24 23:38:05 +03:00
static int _is_partitionable ( struct device * dev )
2004-11-23 14:44:04 +03:00
{
2004-11-24 23:38:05 +03:00
int parts = max_partitions ( MAJOR ( dev - > dev ) ) ;
2004-11-23 14:44:04 +03:00
2009-07-10 02:50:45 +04:00
/* All MD devices are partitionable via blkext (as of 2.6.28) */
if ( MAJOR ( dev - > dev ) = = md_major ( ) )
return 1 ;
2004-12-10 19:01:35 +03:00
if ( ( parts < = 1 ) | | ( MINOR ( dev - > dev ) % parts ) )
2004-11-24 23:38:05 +03:00
return 0 ;
2004-11-23 14:44:04 +03:00
2004-11-24 23:38:05 +03:00
return 1 ;
2004-11-23 14:44:04 +03:00
}
static int _has_partition_table ( struct device * dev )
{
int ret = 0 ;
2004-12-21 19:10:25 +03:00
unsigned p ;
2006-12-01 02:11:42 +03:00
uint16_t buf [ SECTOR_SIZE / sizeof ( uint16_t ) ] ;
2004-12-21 19:10:25 +03:00
uint16_t * part_magic ;
struct partition * part ;
2004-11-23 14:44:04 +03:00
2008-01-30 16:19:47 +03:00
if ( ! dev_read ( dev , UINT64_C ( 0 ) , sizeof ( buf ) , & buf ) )
2010-04-06 21:36:41 +04:00
return_0 ;
2004-12-21 19:10:25 +03:00
/* FIXME Check for other types of partition table too */
/* Check for msdos partition table */
2006-12-01 02:11:42 +03:00
part_magic = buf + PART_MAGIC_OFFSET / sizeof ( buf [ 0 ] ) ;
2004-12-21 19:10:25 +03:00
if ( ( * part_magic = = xlate16 ( PART_MAGIC ) ) ) {
2007-01-16 00:55:11 +03:00
part = ( struct partition * ) ( buf + PART_OFFSET / sizeof ( buf [ 0 ] ) ) ;
2004-12-21 19:10:25 +03:00
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 ;
}
}
2004-11-23 14:44:04 +03:00
return ret ;
}
int is_partitioned_dev ( struct device * dev )
{
2004-11-24 23:38:05 +03:00
if ( ! _is_partitionable ( dev ) )
2004-11-23 14:44:04 +03:00
return 0 ;
return _has_partition_table ( dev ) ;
}
2002-11-18 17:01:16 +03:00
#if 0
2001-09-25 16:49:28 +04:00
# include <sys/stat.h>
# include <sys/mman.h>
# include <stdio.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
2002-11-18 17:01:16 +03:00
2001-09-25 16:49:28 +04:00
# include <errno.h>
# include <sys/ioctl.h>
2002-11-18 17:01:16 +03:00
# include <linux/fs.h>
# include <linux/major.h>
# include <linux/genhd.h>
2001-10-01 19:59:40 +04:00
2001-10-03 16:41:29 +04:00
int _get_partition_type ( struct dev_filter * filter , struct device * d ) ;
2001-09-25 16:49:28 +04:00
2004-11-23 14:44:04 +03:00
# define MINOR_PART(dev) (MINOR((dev)->dev) % max_partitions(MINOR((dev)->dev)))
2001-10-01 19:59:40 +04:00
2004-11-23 14:44:04 +03:00
int is_extended_partition ( struct device * d )
2001-10-01 19:59:40 +04:00
{
2004-11-23 14:44:04 +03:00
return ( MINOR_PART ( d ) > 4 ) ? 1 : 0 ;
2001-10-01 19:59:40 +04:00
}
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 ) ) ;
}
2001-09-25 16:49:28 +04:00
return ret ;
2001-10-01 19:59:40 +04:00
}
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 ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( buffer = dm_malloc ( SECTOR_SIZE ) ) ) {
2001-10-01 19:59:40 +04:00
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 ;
2001-09-25 16:49:28 +04:00
}
2001-10-04 14:13:07 +04:00
# endif
2009-08-01 21:07:36 +04:00
# ifdef linux
2009-08-01 21:11:02 +04:00
int get_primary_dev ( const char * sysfs_dir ,
struct device * dev , dev_t * result )
2009-08-01 21:08:43 +04:00
{
char path [ PATH_MAX + 1 ] ;
2009-08-01 21:09:48 +04:00
char temp_path [ PATH_MAX + 1 ] ;
char buffer [ 64 ] ;
2009-08-01 21:08:43 +04:00
struct stat info ;
2009-08-01 21:09:48 +04:00
FILE * fp ;
uint32_t pri_maj , pri_min ;
int ret = 0 ;
2009-08-01 21:08:43 +04:00
/* 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 " ) ;
2009-08-01 21:09:48 +04:00
return ret ;
2009-08-01 21:08:43 +04:00
}
2009-08-01 21:14:52 +04:00
if ( stat ( path , & info ) = = - 1 ) {
if ( errno ! = ENOENT )
log_sys_error ( " stat " , path ) ;
2009-08-01 21:09:48 +04:00
return ret ;
2009-08-01 21:14:52 +04:00
}
2009-08-01 21:09:48 +04:00
/*
* 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 ;
}
2009-08-01 21:08:43 +04:00
2009-08-01 21:09:48 +04:00
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 */
2009-08-01 21:14:52 +04:00
if ( stat ( path , & info ) = = - 1 ) {
if ( errno = = ENOENT )
log_error ( " sysfs file %s does not exist " , path ) ;
else
log_sys_error ( " stat " , path ) ;
2009-08-01 21:09:48 +04:00
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 ;
2009-08-01 21:08:43 +04:00
}
2009-08-01 21:07:36 +04:00
static unsigned long _dev_topology_attribute ( const char * attribute ,
const char * sysfs_dir ,
struct device * dev )
{
2009-08-01 21:08:43 +04:00
const char * sysfs_fmt_str = " %s/dev/block/%d:%d/%s " ;
2009-08-01 21:07:36 +04:00
char path [ PATH_MAX + 1 ] , buffer [ 64 ] ;
FILE * fp ;
struct stat info ;
2009-08-01 21:08:43 +04:00
dev_t uninitialized_var ( primary ) ;
2009-08-01 21:07:36 +04:00
unsigned long result = 0UL ;
if ( ! attribute | | ! * attribute )
return_0 ;
if ( ! sysfs_dir | | ! * sysfs_dir )
return_0 ;
2009-08-01 21:08:43 +04:00
if ( dm_snprintf ( path , PATH_MAX , sysfs_fmt_str , sysfs_dir ,
( int ) MAJOR ( dev - > dev ) , ( int ) MINOR ( dev - > dev ) ,
2009-08-01 21:07:36 +04:00
attribute ) < 0 ) {
log_error ( " dm_snprintf %s failed " , attribute ) ;
return 0 ;
}
2009-08-01 21:08:43 +04:00
/*
* check if the desired sysfs attribute exists
* - if not : either the kernel doesn ' t have topology support
* or the device could be a partition
*/
2009-08-01 21:14:52 +04:00
if ( stat ( path , & info ) = = - 1 ) {
if ( errno ! = ENOENT ) {
log_sys_error ( " stat " , path ) ;
return 0 ;
}
2009-08-01 21:11:02 +04:00
if ( ! get_primary_dev ( sysfs_dir , dev , & primary ) )
2009-08-01 21:08:43 +04:00
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 ;
}
2009-08-01 21:14:52 +04:00
if ( stat ( path , & info ) = = - 1 ) {
if ( errno ! = ENOENT )
log_sys_error ( " stat " , path ) ;
2009-08-01 21:08:43 +04:00
return 0 ;
2009-08-01 21:14:52 +04:00
}
2009-08-01 21:08:43 +04:00
}
2009-08-01 21:07:36 +04:00
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 ) ;
}
2009-08-01 21:08:43 +04:00
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 ) ;
}
2009-08-01 21:07:36 +04:00
# else
2009-08-01 21:11:02 +04:00
int get_primary_dev ( const char * sysfs_dir ,
struct device * dev , dev_t * result )
{
return 0 ;
}
2009-08-01 21:07:36 +04:00
unsigned long dev_alignment_offset ( const char * sysfs_dir ,
struct device * dev )
{
return 0UL ;
}
2009-08-01 21:08:43 +04:00
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 ;
}
2009-08-01 21:07:36 +04:00
# endif