2001-09-25 16:49:28 +04:00
/*
2013-06-12 14:08:56 +04:00
* Copyright ( C ) 2013 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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2001-09-25 16:49:28 +04:00
*/
2004-11-23 14:44:04 +03:00
# include "lib.h"
2013-06-12 14:08:56 +04:00
# include "dev-type.h"
2004-11-28 01:07:41 +03:00
# include "xlate.h"
2013-06-12 14:08:56 +04:00
# include "config.h"
# include "metadata.h"
2004-11-23 14:44:04 +03:00
2013-06-12 14:08:56 +04:00
# include <libgen.h>
# include <ctype.h>
2010-12-15 15:49:55 +03:00
2013-11-15 17:50:02 +04:00
# ifdef BLKID_WIPING_SUPPORT
2014-03-06 19:03:08 +04:00
# include <blkid.h>
2013-11-15 17:50:02 +04:00
# endif
2014-09-03 17:49:36 +04:00
# ifdef UDEV_SYNC_SUPPORT
# include <libudev.h>
2015-01-29 18:44:34 +03:00
# include "dev-ext-udev-constants.h"
2014-09-03 17:49:36 +04:00
# endif
2013-06-12 14:08:56 +04:00
# include "device-types.h"
struct dev_types * create_dev_types ( const char * proc_dir ,
const struct dm_config_node * cn )
{
struct dev_types * dt ;
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 ( ! ( dt = dm_zalloc ( sizeof ( struct dev_types ) ) ) ) {
log_error ( " Failed to allocate device type register. " ) ;
return NULL ;
}
if ( ! * proc_dir ) {
log_verbose ( " No proc filesystem found: using all block device types " ) ;
for ( i = 0 ; i < NUMBER_OF_MAJORS ; i + + )
dt - > dev_type_array [ i ] . max_partitions = 1 ;
return dt ;
}
if ( dm_snprintf ( proc_devices , sizeof ( proc_devices ) ,
" %s/devices " , proc_dir ) < 0 ) {
log_error ( " Failed to create /proc/devices string " ) ;
goto bad ;
}
if ( ! ( pd = fopen ( proc_devices , " r " ) ) ) {
log_sys_error ( " fopen " , proc_devices ) ;
goto bad ;
}
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 ) ) )
dt - > md_major = line_maj ;
/* Look for blkext device */
if ( ! strncmp ( " blkext " , line + i , 6 ) & & isspace ( * ( line + i + 6 ) ) )
dt - > blkext_major = line_maj ;
/* Look for drbd device */
if ( ! strncmp ( " drbd " , line + i , 4 ) & & isspace ( * ( line + i + 4 ) ) )
dt - > drbd_major = line_maj ;
2015-01-23 23:01:34 +03:00
/* Look for DASD */
if ( ! strncmp ( " dasd " , line + i , 4 ) & & isspace ( * ( line + i + 4 ) ) )
dt - > dasd_major = line_maj ;
2013-06-12 14:08:56 +04:00
/* Look for EMC powerpath */
if ( ! strncmp ( " emcpower " , line + i , 8 ) & & isspace ( * ( line + i + 8 ) ) )
dt - > emcpower_major = line_maj ;
if ( ! strncmp ( " power2 " , line + i , 6 ) & & isspace ( * ( line + i + 6 ) ) )
dt - > 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 ) ) )
dt - > device_mapper_major = line_maj ;
/* Major is SCSI device */
if ( ! strncmp ( " sd " , line + i , 2 ) & & isspace ( * ( line + i + 2 ) ) )
dt - > dev_type_array [ 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 ; _dev_known_types [ j ] . name [ 0 ] ; j + + ) {
dev_len = strlen ( _dev_known_types [ j ] . name ) ;
if ( dev_len < = strlen ( line + i ) & &
! strncmp ( _dev_known_types [ j ] . name , line + i , dev_len ) & &
( line_maj < NUMBER_OF_MAJORS ) ) {
dt - > dev_type_array [ line_maj ] . max_partitions =
_dev_known_types [ 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 ) ;
goto bad ;
}
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 ) ;
goto bad ;
}
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 ) ;
goto bad ;
}
if ( dev_len < = strlen ( line + i ) & &
! strncmp ( name , line + i , dev_len ) & &
( line_maj < NUMBER_OF_MAJORS ) ) {
dt - > dev_type_array [ line_maj ] . max_partitions = cv - > v . i ;
break ;
}
}
}
if ( fclose ( pd ) )
log_sys_error ( " fclose " , proc_devices ) ;
return dt ;
bad :
dm_free ( dt ) ;
return NULL ;
}
int dev_subsystem_part_major ( struct dev_types * dt , struct device * dev )
{
dev_t primary_dev ;
if ( MAJOR ( dev - > dev ) = = dt - > device_mapper_major )
return 1 ;
if ( MAJOR ( dev - > dev ) = = dt - > drbd_major )
return 1 ;
if ( MAJOR ( dev - > dev ) = = dt - > emcpower_major )
return 1 ;
if ( MAJOR ( dev - > dev ) = = dt - > power2_major )
return 1 ;
2004-12-21 19:10:25 +03:00
2013-06-12 14:08:56 +04:00
if ( ( MAJOR ( dev - > dev ) = = dt - > blkext_major ) & &
2013-06-12 16:33:28 +04:00
dev_get_primary_dev ( dt , dev , & primary_dev ) & &
2013-06-12 14:08:56 +04:00
( MAJOR ( primary_dev ) = = dt - > md_major ) )
return 1 ;
return 0 ;
}
const char * dev_subsystem_name ( struct dev_types * dt , struct device * dev )
{
2015-04-24 22:58:58 +03:00
if ( MAJOR ( dev - > dev ) = = dt - > device_mapper_major )
return " DM " ;
2013-06-12 14:08:56 +04:00
if ( MAJOR ( dev - > dev ) = = dt - > md_major )
return " MD " ;
if ( MAJOR ( dev - > dev ) = = dt - > drbd_major )
return " DRBD " ;
2015-01-23 23:01:34 +03:00
if ( MAJOR ( dev - > dev ) = = dt - > dasd_major )
return " DASD " ;
2013-06-12 14:08:56 +04:00
if ( MAJOR ( dev - > dev ) = = dt - > emcpower_major )
return " EMCPOWER " ;
if ( MAJOR ( dev - > dev ) = = dt - > power2_major )
return " POWER2 " ;
if ( MAJOR ( dev - > dev ) = = dt - > blkext_major )
return " BLKEXT " ;
return " " ;
}
int major_max_partitions ( struct dev_types * dt , int major )
{
if ( major > = NUMBER_OF_MAJORS )
return 0 ;
return dt - > dev_type_array [ major ] . max_partitions ;
}
int major_is_scsi_device ( struct dev_types * dt , int major )
{
if ( major > = NUMBER_OF_MAJORS )
return 0 ;
return ( dt - > dev_type_array [ major ] . flags & PARTITION_SCSI_DEVICE ) ? 1 : 0 ;
}
/* 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
2013-06-12 14:08:56 +04:00
static int _is_partitionable ( struct dev_types * dt , struct device * dev )
2004-11-23 14:44:04 +03:00
{
2013-06-12 14:08:56 +04:00
int parts = major_max_partitions ( dt , MAJOR ( dev - > dev ) ) ;
2004-11-23 14:44:04 +03:00
2015-01-12 16:02:57 +03:00
if ( MAJOR ( dev - > dev ) = = dt - > device_mapper_major )
return 1 ;
2009-07-10 02:50:45 +04:00
/* All MD devices are partitionable via blkext (as of 2.6.28) */
2013-06-12 14:08:56 +04:00
if ( MAJOR ( dev - > dev ) = = dt - > md_major )
2009-07-10 02:50:45 +04:00
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 ;
2010-10-20 19:07:30 +04:00
struct {
uint8_t skip [ PART_OFFSET ] ;
struct partition part [ 4 ] ;
uint16_t magic ;
} __attribute__ ( ( packed ) ) buf ; /* sizeof() == SECTOR_SIZE */
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 */
2010-10-20 19:07:30 +04:00
if ( buf . magic = = xlate16 ( PART_MAGIC ) ) {
for ( p = 0 ; p < 4 ; + + p ) {
2004-12-21 19:10:25 +03:00
/* Table is invalid if boot indicator not 0 or 0x80 */
2010-10-20 19:07:30 +04:00
if ( buf . part [ p ] . boot_ind & 0x7f ) {
2004-12-21 19:10:25 +03:00
ret = 0 ;
break ;
}
/* Must have at least one non-empty partition */
2010-10-20 19:07:30 +04:00
if ( buf . part [ p ] . nr_sects )
2004-12-21 19:10:25 +03:00
ret = 1 ;
}
}
2004-11-23 14:44:04 +03:00
return ret ;
}
2014-09-03 17:49:36 +04:00
# ifdef UDEV_SYNC_SUPPORT
2016-02-02 15:28:11 +03:00
static int _udev_dev_is_partitioned ( struct dev_types * dt , struct device * dev )
2014-09-03 17:49:36 +04:00
{
struct dev_ext * ext ;
2016-02-02 15:28:11 +03:00
struct udev_device * device ;
const char * value ;
2014-09-03 17:49:36 +04:00
if ( ! ( ext = dev_ext_get ( dev ) ) )
return_0 ;
2016-02-02 15:28:11 +03:00
device = ( struct udev_device * ) ext - > handle ;
if ( ! ( value = udev_device_get_property_value ( device , DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE ) ) )
2014-09-03 17:49:36 +04:00
return 0 ;
2016-02-02 15:28:11 +03:00
/*
* Device - mapper devices have DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE
* variable set if there ' s partition table found on whole device .
* Partitions do not have this variable set - it ' s enough to use
* only this variable to decide whether this device has partition
* table on it .
*/
if ( MAJOR ( dev - > dev ) = = dt - > device_mapper_major )
return 1 ;
2014-09-03 17:49:36 +04:00
2016-02-02 15:28:11 +03:00
/*
* Other devices have DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE set for
* * both * whole device and partitions . We need to look at the
* DEV_EXT_UDEV_DEVTYPE in addition to decide - whole device
* with partition table on it has this variable set to
* DEV_EXT_UDEV_DEVTYPE_DISK .
*/
if ( ! ( value = udev_device_get_property_value ( device , DEV_EXT_UDEV_DEVTYPE ) ) )
return_0 ;
return ! strcmp ( value , DEV_EXT_UDEV_DEVTYPE_DISK ) ;
2014-09-03 17:49:36 +04:00
}
# else
2016-02-02 15:28:11 +03:00
static int _udev_dev_is_partitioned ( struct dev_types * dt , struct device * dev )
2014-09-03 17:49:36 +04:00
{
return 0 ;
}
# endif
static int _native_dev_is_partitioned ( struct dev_types * dt , struct device * dev )
2004-11-23 14:44:04 +03:00
{
2014-09-03 17:49:36 +04:00
int r ;
2013-06-12 14:08:56 +04:00
if ( ! _is_partitionable ( dt , dev ) )
2004-11-23 14:44:04 +03:00
return 0 ;
2015-01-23 23:01:34 +03:00
/* Unpartitioned DASD devices are not supported. */
2015-10-12 10:09:11 +03:00
if ( ( MAJOR ( dev - > dev ) = = dt - > dasd_major ) & & dasd_is_cdl_formatted ( dev ) )
2015-01-23 23:01:34 +03:00
return 1 ;
2014-09-03 17:49:36 +04:00
if ( ! dev_open_readonly_quiet ( dev ) ) {
log_debug_devs ( " %s: failed to open device, considering device "
" is partitioned " , dev_name ( dev ) ) ;
return 1 ;
}
r = _has_partition_table ( dev ) ;
if ( ! dev_close ( dev ) )
stack ;
return r ;
}
int dev_is_partitioned ( struct dev_types * dt , struct device * dev )
{
if ( dev - > ext . src = = DEV_EXT_NONE )
return _native_dev_is_partitioned ( dt , dev ) ;
if ( dev - > ext . src = = DEV_EXT_UDEV )
2016-02-02 15:28:11 +03:00
return _udev_dev_is_partitioned ( dt , dev ) ;
2014-09-03 17:49:36 +04:00
log_error ( INTERNAL_ERROR " Missing hook for partition table recognition "
" using external device info source %s " , dev_ext_name ( dev ) ) ;
return 0 ;
2004-11-23 14:44:04 +03:00
}
2013-06-12 14:14:11 +04:00
/*
* Get primary dev for the dev supplied .
2013-06-12 16:33:28 +04:00
*
* We can get a primary device for a partition either by :
* A : knowing the number of partitions allowed for the dev and also
* which major : minor number represents the primary and partition device
* ( by using the dev_types - > dev_type_array )
* B : by the existence of the ' partition ' sysfs attribute
* ( / dev / block / < major > : < minor > / partition )
*
* Method A is tried first , then method B as a fallback if A fails .
*
* N . B . Method B can only do the decision based on the pure existence of
* the ' partition ' sysfs item . There ' s no direct scan for partition
* tables whatsoever !
*
2013-06-12 14:14:11 +04:00
* Returns :
2013-06-12 16:33:28 +04:00
* 0 on error
* 1 if the dev is already a primary dev , primary dev in ' result '
* 2 if the dev is a partition , primary dev in ' result '
2013-06-12 14:14:11 +04:00
*/
2013-06-12 14:08:56 +04:00
int dev_get_primary_dev ( struct dev_types * dt , struct device * dev , dev_t * result )
{
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2013-06-12 14:14:11 +04:00
int major = ( int ) MAJOR ( dev - > dev ) ;
int minor = ( int ) MINOR ( dev - > dev ) ;
2014-08-19 13:35:18 +04:00
char path [ PATH_MAX ] ;
char temp_path [ PATH_MAX ] ;
2013-06-12 14:08:56 +04:00
char buffer [ 64 ] ;
struct stat info ;
2013-06-12 14:14:11 +04:00
FILE * fp = NULL ;
2013-07-19 17:26:53 +04:00
int parts , residue , size , ret = 0 ;
2013-06-12 14:14:11 +04:00
/*
* Try to get the primary dev out of the
* list of known device types first .
*/
if ( ( parts = dt - > dev_type_array [ major ] . max_partitions ) > 1 ) {
if ( ( residue = minor % parts ) ) {
2015-05-07 12:03:48 +03:00
* result = MKDEV ( ( dev_t ) major , ( dev_t ) ( minor - residue ) ) ;
2013-06-12 16:33:28 +04:00
ret = 2 ;
2013-06-12 14:14:11 +04:00
} else {
* result = dev - > dev ;
2013-06-12 16:33:28 +04:00
ret = 1 ; /* dev is not a partition! */
2013-06-12 14:14:11 +04:00
}
goto out ;
}
/*
* If we can ' t get the primary dev out of the list of known device
* types , try to look at sysfs directly then . This is more complex
* way and it also requires certain sysfs layout to be present
* which might not be there in old kernels !
*/
2013-06-12 14:08:56 +04:00
/* check if dev is a partition */
2014-08-19 13:35:18 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/dev/block/%d:%d/partition " ,
2013-06-12 14:14:11 +04:00
sysfs_dir , major , minor ) < 0 ) {
2013-06-12 14:08:56 +04:00
log_error ( " dm_snprintf partition failed " ) ;
2013-06-12 14:14:11 +04:00
goto out ;
2013-06-12 14:08:56 +04:00
}
if ( stat ( path , & info ) = = - 1 ) {
if ( errno ! = ENOENT )
log_sys_error ( " stat " , path ) ;
2013-06-12 14:14:11 +04:00
* result = dev - > dev ;
2013-07-19 17:26:53 +04:00
ret = 1 ;
goto out ; /* dev is not a partition! */
2013-06-12 14:08:56 +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
*/
2014-08-19 13:35:18 +04:00
if ( ( size = readlink ( dirname ( path ) , temp_path , sizeof ( temp_path ) - 1 ) ) < 0 ) {
2013-06-12 14:08:56 +04:00
log_sys_error ( " readlink " , path ) ;
2013-06-12 14:14:11 +04:00
goto out ;
2013-06-12 14:08:56 +04:00
}
temp_path [ size ] = ' \0 ' ;
2014-08-19 13:35:18 +04:00
if ( dm_snprintf ( path , sizeof ( path ) , " %s/block/%s/dev " ,
2013-06-12 14:08:56 +04:00
sysfs_dir , basename ( dirname ( temp_path ) ) ) < 0 ) {
log_error ( " dm_snprintf dev failed " ) ;
2013-06-12 14:14:11 +04:00
goto out ;
2013-06-12 14:08:56 +04:00
}
/* finally, parse 'dev' attribute and create corresponding dev_t */
2015-11-08 19:15:24 +03:00
if ( ! ( fp = fopen ( path , " r " ) ) ) {
2013-06-12 14:08:56 +04:00
if ( errno = = ENOENT )
2015-11-08 19:15:24 +03:00
log_error ( " sysfs file %s does not exist. " , path ) ;
2013-06-12 14:08:56 +04:00
else
2015-11-08 19:15:24 +03:00
log_sys_error ( " fopen " , path ) ;
2013-06-12 14:14:11 +04:00
goto out ;
2013-06-12 14:08:56 +04:00
}
if ( ! fgets ( buffer , sizeof ( buffer ) , fp ) ) {
log_sys_error ( " fgets " , path ) ;
goto out ;
}
2013-06-12 14:14:11 +04:00
if ( sscanf ( buffer , " %d:%d " , & major , & minor ) ! = 2 ) {
2013-06-12 14:08:56 +04:00
log_error ( " sysfs file %s not in expected MAJ:MIN format: %s " ,
path , buffer ) ;
goto out ;
}
2015-05-07 12:03:48 +03:00
* result = MKDEV ( ( dev_t ) major , ( dev_t ) minor ) ;
2013-06-23 13:14:34 +04:00
ret = 2 ;
2013-06-12 14:08:56 +04:00
out :
2013-06-12 14:14:11 +04:00
if ( fp & & fclose ( fp ) )
2013-06-12 14:08:56 +04:00
log_sys_error ( " fclose " , path ) ;
return ret ;
}
2013-11-15 17:50:02 +04:00
# ifdef BLKID_WIPING_SUPPORT
2014-02-10 16:28:13 +04:00
static inline int _type_in_flag_list ( const char * type , uint32_t flag_list )
{
return ( ( ( flag_list & TYPE_LVM2_MEMBER ) & & ! strcmp ( type , " LVM2_member " ) ) | |
( ( flag_list & TYPE_LVM1_MEMBER ) & & ! strcmp ( type , " LVM1_member " ) ) | |
( ( flag_list & TYPE_DM_SNAPSHOT_COW ) & & ! strcmp ( type , " DM_snapshot_cow " ) ) ) ;
}
2015-07-21 11:34:04 +03:00
# define MSG_FAILED_SIG_OFFSET "Failed to get offset of the %s signature on %s."
# define MSG_FAILED_SIG_LENGTH "Failed to get length of the %s signature on %s."
2015-07-21 12:00:43 +03:00
# define MSG_WIPING_SKIPPED " Wiping skipped."
2015-07-21 11:34:04 +03:00
2013-11-28 16:14:46 +04:00
static int _blkid_wipe ( blkid_probe probe , struct device * dev , const char * name ,
2014-02-10 16:28:13 +04:00
uint32_t types_to_exclude , uint32_t types_no_prompt ,
int yes , force_t force )
2013-11-15 17:50:02 +04:00
{
2014-09-22 17:51:49 +04:00
static const char _msg_wiping [ ] = " Wiping %s signature on %s. " ;
2013-11-15 17:50:02 +04:00
const char * offset = NULL , * type = NULL , * magic = NULL ,
* usage = NULL , * label = NULL , * uuid = NULL ;
loff_t offset_value ;
size_t len ;
if ( ! blkid_probe_lookup_value ( probe , " TYPE " , & type , NULL ) ) {
2014-02-10 16:28:13 +04:00
if ( _type_in_flag_list ( type , types_to_exclude ) )
2015-02-19 15:20:01 +03:00
return 2 ;
2014-01-22 19:26:49 +04:00
if ( blkid_probe_lookup_value ( probe , " SBMAGIC_OFFSET " , & offset , NULL ) ) {
2015-07-21 11:34:04 +03:00
if ( force < DONT_PROMPT ) {
log_error ( MSG_FAILED_SIG_OFFSET , type , name ) ;
return 0 ;
} else {
2015-07-21 12:00:43 +03:00
log_error ( " WARNING: " MSG_FAILED_SIG_OFFSET MSG_WIPING_SKIPPED , type , name ) ;
2015-07-21 11:34:04 +03:00
return 2 ;
}
2014-01-22 19:26:49 +04:00
}
if ( blkid_probe_lookup_value ( probe , " SBMAGIC " , & magic , & len ) ) {
2015-07-21 11:34:04 +03:00
if ( force < DONT_PROMPT ) {
log_error ( MSG_FAILED_SIG_LENGTH , type , name ) ;
return 0 ;
} else {
2015-07-21 12:00:43 +03:00
log_warn ( " WARNING: " MSG_FAILED_SIG_LENGTH MSG_WIPING_SKIPPED , type , name ) ;
2015-07-21 11:34:04 +03:00
return 2 ;
}
2014-01-22 19:26:49 +04:00
}
2013-11-15 17:50:02 +04:00
} else if ( ! blkid_probe_lookup_value ( probe , " PTTYPE " , & type , NULL ) ) {
2014-01-22 19:26:49 +04:00
if ( blkid_probe_lookup_value ( probe , " PTMAGIC_OFFSET " , & offset , NULL ) ) {
2015-07-21 11:34:04 +03:00
if ( force < DONT_PROMPT ) {
log_error ( MSG_FAILED_SIG_OFFSET , type , name ) ;
return 0 ;
} else {
2015-07-21 12:00:43 +03:00
log_warn ( " WARNING: " MSG_FAILED_SIG_OFFSET MSG_WIPING_SKIPPED , type , name ) ;
2015-07-21 11:34:04 +03:00
return 2 ;
}
2014-01-22 19:26:49 +04:00
}
if ( blkid_probe_lookup_value ( probe , " PTMAGIC " , & magic , & len ) ) {
2015-07-21 11:34:04 +03:00
if ( force < DONT_PROMPT ) {
log_error ( MSG_FAILED_SIG_LENGTH , type , name ) ;
return 0 ;
} else {
2015-07-21 12:00:43 +03:00
log_warn ( " WARNING: " MSG_FAILED_SIG_LENGTH MSG_WIPING_SKIPPED , type , name ) ;
2015-07-21 11:34:04 +03:00
return 2 ;
}
2014-01-22 19:26:49 +04:00
}
2013-11-15 17:50:02 +04:00
usage = " partition table " ;
} else
return_0 ;
offset_value = strtoll ( offset , NULL , 10 ) ;
if ( ! usage )
2014-05-07 13:22:14 +04:00
( void ) blkid_probe_lookup_value ( probe , " USAGE " , & usage , NULL ) ;
( void ) blkid_probe_lookup_value ( probe , " LABEL " , & label , NULL ) ;
( void ) blkid_probe_lookup_value ( probe , " UUID " , & uuid , NULL ) ;
/* Return values ignored here, in the worst case we print NULL */
2013-11-15 17:50:02 +04:00
log_verbose ( " Found existing signature on %s at offset %s: LABEL= \" %s \" "
" UUID= \" %s \" TYPE= \" %s \" USAGE= \" %s \" " ,
name , offset , label , uuid , type , usage ) ;
2014-02-10 16:28:13 +04:00
if ( ! _type_in_flag_list ( type , types_no_prompt ) ) {
if ( ! yes & & ( force = = PROMPT ) & &
yes_no_prompt ( " WARNING: %s signature detected on %s at offset %s. "
2014-07-03 14:15:19 +04:00
" Wipe it? [y/n]: " , type , name , offset ) = = ' n ' ) {
log_error ( " Aborted wiping of %s. " , type ) ;
return 0 ;
}
2014-02-10 00:33:36 +04:00
log_print_unless_silent ( _msg_wiping , type , name ) ;
2014-02-10 16:28:13 +04:00
} else
2014-02-10 00:33:36 +04:00
log_verbose ( _msg_wiping , type , name ) ;
2013-11-15 17:50:02 +04:00
if ( ! dev_set ( dev , offset_value , len , 0 ) ) {
log_error ( " Failed to wipe %s signature on %s. " , type , name ) ;
return 0 ;
}
return 1 ;
}
static int _wipe_known_signatures_with_blkid ( struct device * dev , const char * name ,
2014-02-10 16:28:13 +04:00
uint32_t types_to_exclude ,
uint32_t types_no_prompt ,
2015-02-17 11:46:34 +03:00
int yes , force_t force , int * wiped )
2013-11-15 17:50:02 +04:00
{
blkid_probe probe = NULL ;
2015-02-17 11:46:34 +03:00
int found = 0 , left = 0 , wiped_tmp ;
2015-02-19 15:20:01 +03:00
int r_wipe ;
2013-11-15 17:50:02 +04:00
int r = 0 ;
2015-02-17 11:46:34 +03:00
if ( ! wiped )
wiped = & wiped_tmp ;
* wiped = 0 ;
2013-11-15 17:50:02 +04:00
/* TODO: Should we check for valid dev - _dev_is_valid(dev)? */
if ( ! ( probe = blkid_new_probe_from_filename ( dev_name ( dev ) ) ) ) {
log_error ( " Failed to create a new blkid probe for device %s. " , dev_name ( dev ) ) ;
goto out ;
}
blkid_probe_enable_partitions ( probe , 1 ) ;
blkid_probe_set_partitions_flags ( probe , BLKID_PARTS_MAGIC ) ;
blkid_probe_enable_superblocks ( probe , 1 ) ;
blkid_probe_set_superblocks_flags ( probe , BLKID_SUBLKS_LABEL |
BLKID_SUBLKS_UUID |
BLKID_SUBLKS_TYPE |
BLKID_SUBLKS_USAGE |
BLKID_SUBLKS_VERSION |
BLKID_SUBLKS_MAGIC |
BLKID_SUBLKS_BADCSUM ) ;
while ( ! blkid_do_probe ( probe ) ) {
2015-10-13 13:18:04 +03:00
if ( ( r_wipe = _blkid_wipe ( probe , dev , name , types_to_exclude , types_no_prompt , yes , force ) ) = = 1 ) {
2015-02-17 11:46:34 +03:00
( * wiped ) + + ;
2015-10-13 13:18:04 +03:00
if ( blkid_probe_step_back ( probe ) ) {
log_error ( " Failed to step back blkid probe to check just wiped signature. " ) ;
goto out ;
}
}
2015-02-19 15:20:01 +03:00
/* do not count excluded types */
if ( r_wipe ! = 2 )
found + + ;
2013-11-15 17:50:02 +04:00
}
if ( ! found )
r = 1 ;
2015-02-17 11:46:34 +03:00
left = found - * wiped ;
2013-11-15 17:50:02 +04:00
if ( ! left )
r = 1 ;
else
log_warn ( " %d existing signature%s left on the device. " ,
left , left > 1 ? " s " : " " ) ;
out :
if ( probe )
blkid_free_probe ( probe ) ;
return r ;
}
# endif /* BLKID_WIPING_SUPPORT */
2013-11-06 18:09:29 +04:00
static int _wipe_signature ( struct device * dev , const char * type , const char * name ,
2015-02-17 11:46:34 +03:00
int wipe_len , int yes , force_t force , int * wiped ,
2013-11-06 18:09:29 +04:00
int ( * signature_detection_fn ) ( struct device * dev , uint64_t * offset_found ) )
{
int wipe ;
uint64_t offset_found ;
wipe = signature_detection_fn ( dev , & offset_found ) ;
if ( wipe = = - 1 ) {
log_error ( " Fatal error while trying to detect %s on %s. " ,
type , name ) ;
return 0 ;
}
if ( wipe = = 0 )
return 1 ;
/* Specifying --yes => do not ask. */
if ( ! yes & & ( force = = PROMPT ) & &
2014-07-03 14:15:19 +04:00
yes_no_prompt ( " WARNING: %s detected on %s. Wipe it? [y/n]: " ,
type , name ) = = ' n ' ) {
log_error ( " Aborted wiping of %s. " , type ) ;
return 0 ;
}
2013-11-06 18:09:29 +04:00
log_print_unless_silent ( " Wiping %s on %s. " , type , name ) ;
if ( ! dev_set ( dev , offset_found , wipe_len , 0 ) ) {
log_error ( " Failed to wipe %s on %s. " , type , name ) ;
return 0 ;
}
2015-02-17 11:46:34 +03:00
( * wiped ) + + ;
2013-11-06 18:09:29 +04:00
return 1 ;
}
2013-11-15 17:50:02 +04:00
static int _wipe_known_signatures_with_lvm ( struct device * dev , const char * name ,
2014-02-10 16:28:13 +04:00
uint32_t types_to_exclude __attribute__ ( ( unused ) ) ,
uint32_t types_no_prompt __attribute__ ( ( unused ) ) ,
2015-02-17 11:46:34 +03:00
int yes , force_t force , int * wiped )
2013-11-06 18:09:29 +04:00
{
2015-02-17 11:46:34 +03:00
int wiped_tmp ;
if ( ! wiped )
wiped = & wiped_tmp ;
* wiped = 0 ;
if ( ! _wipe_signature ( dev , " software RAID md superblock " , name , 4 , yes , force , wiped , dev_is_md ) | |
! _wipe_signature ( dev , " swap signature " , name , 10 , yes , force , wiped , dev_is_swap ) | |
! _wipe_signature ( dev , " LUKS signature " , name , 8 , yes , force , wiped , dev_is_luks ) )
2013-11-06 18:09:29 +04:00
return 0 ;
return 1 ;
}
2013-11-15 17:50:02 +04:00
int wipe_known_signatures ( struct cmd_context * cmd , struct device * dev ,
2014-02-10 16:28:13 +04:00
const char * name , uint32_t types_to_exclude ,
2015-02-17 11:46:34 +03:00
uint32_t types_no_prompt , int yes , force_t force ,
int * wiped )
2013-11-15 17:50:02 +04:00
{
2015-09-22 12:11:26 +03:00
int blkid_wiping_enabled = find_config_tree_bool ( cmd , allocation_use_blkid_wiping_CFG , NULL ) ;
2013-11-15 17:50:02 +04:00
# ifdef BLKID_WIPING_SUPPORT
2015-09-22 12:11:26 +03:00
if ( blkid_wiping_enabled )
2013-11-28 16:14:46 +04:00
return _wipe_known_signatures_with_blkid ( dev , name ,
2014-02-10 16:28:13 +04:00
types_to_exclude ,
types_no_prompt ,
2015-02-17 11:46:34 +03:00
yes , force , wiped ) ;
2013-11-15 17:50:02 +04:00
# endif
2015-09-22 12:11:26 +03:00
if ( blkid_wiping_enabled ) {
log_warn ( " allocation/use_blkid_wiping=1 configuration setting is set "
" while LVM is not compiled with blkid wiping support. " ) ;
log_warn ( " Falling back to native LVM signature detection. " ) ;
}
2013-11-28 16:14:46 +04:00
return _wipe_known_signatures_with_lvm ( dev , name ,
2014-02-10 16:28:13 +04:00
types_to_exclude ,
types_no_prompt ,
2015-02-17 11:46:34 +03:00
yes , force , wiped ) ;
2013-11-15 17:50:02 +04:00
}
2013-11-13 17:56:29 +04:00
# ifdef __linux__
2009-08-01 21:07:36 +04:00
2013-11-22 15:57:35 +04:00
static int _snprintf_attr ( char * buf , size_t buf_size , const char * sysfs_dir ,
const char * attribute , dev_t dev )
{
if ( dm_snprintf ( buf , buf_size , " %s/dev/block/%d:%d/%s " , sysfs_dir ,
( int ) MAJOR ( dev ) , ( int ) MINOR ( dev ) ,
attribute ) < 0 ) {
log_warn ( " dm_snprintf %s failed. " , attribute ) ;
return 0 ;
}
return 1 ;
}
2013-06-12 14:08:56 +04:00
static unsigned long _dev_topology_attribute ( struct dev_types * dt ,
const char * attribute ,
2014-09-16 03:44:25 +04:00
struct device * dev ,
unsigned long default_value )
2009-08-01 21:07:36 +04:00
{
2013-06-12 13:38:48 +04:00
const char * sysfs_dir = dm_sysfs_dir ( ) ;
2013-11-22 15:57:35 +04:00
char path [ PATH_MAX ] , buffer [ 64 ] ;
2009-08-01 21:07:36 +04:00
FILE * fp ;
struct stat info ;
2009-08-01 21:08:43 +04:00
dev_t uninitialized_var ( primary ) ;
2014-09-16 03:44:25 +04:00
unsigned long result = default_value ;
unsigned long value = 0UL ;
2009-08-01 21:07:36 +04:00
if ( ! attribute | | ! * attribute )
2014-09-16 03:44:25 +04:00
goto_out ;
2009-08-01 21:07:36 +04:00
if ( ! sysfs_dir | | ! * sysfs_dir )
2014-09-16 03:44:25 +04:00
goto_out ;
2009-08-01 21:07:36 +04:00
2013-11-22 15:57:35 +04:00
if ( ! _snprintf_attr ( path , sizeof ( path ) , sysfs_dir , attribute , dev - > dev ) )
2014-09-16 03:44:25 +04:00
goto_out ;
2009-08-01 21:07:36 +04:00
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 ) {
2014-07-03 14:03:16 +04:00
log_sys_debug ( " stat " , path ) ;
2014-09-16 03:44:25 +04:00
goto out ;
2009-08-01 21:14:52 +04:00
}
2013-06-12 16:33:28 +04:00
if ( ! dev_get_primary_dev ( dt , dev , & primary ) )
2014-09-16 03:44:25 +04:00
goto out ;
2009-08-01 21:08:43 +04:00
/* get attribute from partition's primary device */
2013-11-22 15:57:35 +04:00
if ( ! _snprintf_attr ( path , sizeof ( path ) , sysfs_dir , attribute , primary ) )
2014-09-16 03:44:25 +04:00
goto_out ;
2013-11-22 15:57:35 +04:00
2009-08-01 21:14:52 +04:00
if ( stat ( path , & info ) = = - 1 ) {
if ( errno ! = ENOENT )
2014-07-03 14:03:16 +04:00
log_sys_debug ( " stat " , path ) ;
2014-09-16 03:44:25 +04:00
goto out ;
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 " ) ) ) {
2014-07-03 14:03:16 +04:00
log_sys_debug ( " fopen " , path ) ;
2014-09-16 03:44:25 +04:00
goto out ;
2009-08-01 21:07:36 +04:00
}
if ( ! fgets ( buffer , sizeof ( buffer ) , fp ) ) {
2014-07-03 14:03:16 +04:00
log_sys_debug ( " fgets " , path ) ;
2014-09-16 03:44:25 +04:00
goto out_close ;
2009-08-01 21:07:36 +04:00
}
2014-09-16 03:44:25 +04:00
if ( sscanf ( buffer , " %lu " , & value ) ! = 1 ) {
2014-07-03 14:03:16 +04:00
log_warn ( " sysfs file %s not in expected format: %s " , path , buffer ) ;
2014-09-16 03:44:25 +04:00
goto out_close ;
2009-08-01 21:07:36 +04:00
}
2014-09-16 03:44:25 +04:00
log_very_verbose ( " Device %s: %s is %lu%s. " ,
2015-03-27 15:43:24 +03:00
dev_name ( dev ) , attribute , value , default_value ? " " : " bytes " ) ;
2009-08-01 21:07:36 +04:00
2014-09-16 03:44:25 +04:00
result = value > > SECTOR_SHIFT ;
2016-03-03 22:53:08 +03:00
if ( ! result & & value ) {
2016-03-11 03:19:16 +03:00
log_warn ( " WARNING: Device %s: %s is %lu and is unexpectedly less than sector. " ,
2016-03-03 22:53:08 +03:00
dev_name ( dev ) , attribute , value ) ;
result = 1 ;
}
2014-09-16 03:44:25 +04:00
out_close :
2009-08-01 21:07:36 +04:00
if ( fclose ( fp ) )
2014-07-03 14:03:16 +04:00
log_sys_debug ( " fclose " , path ) ;
2009-08-01 21:07:36 +04:00
2014-09-16 03:44:25 +04:00
out :
return result ;
2009-08-01 21:07:36 +04:00
}
2013-06-12 14:08:56 +04:00
unsigned long dev_alignment_offset ( struct dev_types * dt , struct device * dev )
2009-08-01 21:07:36 +04:00
{
2014-09-16 03:44:25 +04:00
return _dev_topology_attribute ( dt , " alignment_offset " , dev , 0UL ) ;
2009-08-01 21:07:36 +04:00
}
2013-06-12 14:08:56 +04:00
unsigned long dev_minimum_io_size ( struct dev_types * dt , struct device * dev )
2009-08-01 21:08:43 +04:00
{
2014-09-16 03:44:25 +04:00
return _dev_topology_attribute ( dt , " queue/minimum_io_size " , dev , 0UL ) ;
2009-08-01 21:08:43 +04:00
}
2013-06-12 14:08:56 +04:00
unsigned long dev_optimal_io_size ( struct dev_types * dt , struct device * dev )
2009-08-01 21:08:43 +04:00
{
2014-09-16 03:44:25 +04:00
return _dev_topology_attribute ( dt , " queue/optimal_io_size " , dev , 0UL ) ;
2009-08-01 21:08:43 +04:00
}
2013-06-12 14:08:56 +04:00
unsigned long dev_discard_max_bytes ( struct dev_types * dt , struct device * dev )
2011-04-13 01:59:01 +04:00
{
2014-09-16 03:44:25 +04:00
return _dev_topology_attribute ( dt , " queue/discard_max_bytes " , dev , 0UL ) ;
2011-04-13 01:59:01 +04:00
}
2013-06-12 14:08:56 +04:00
unsigned long dev_discard_granularity ( struct dev_types * dt , struct device * dev )
2011-04-13 01:59:01 +04:00
{
2014-09-16 03:44:25 +04:00
return _dev_topology_attribute ( dt , " queue/discard_granularity " , dev , 0UL ) ;
2011-04-13 01:59:01 +04:00
}
2014-09-16 03:44:25 +04:00
int dev_is_rotational ( struct dev_types * dt , struct device * dev )
{
return ( int ) _dev_topology_attribute ( dt , " queue/rotational " , dev , 1UL ) ;
}
2009-08-01 21:07:36 +04:00
# else
2013-06-17 17:17:15 +04:00
int dev_get_primary_dev ( struct dev_types * dt , struct device * dev , dev_t * result )
2009-08-01 21:11:02 +04:00
{
return 0 ;
}
2013-06-12 14:08:56 +04:00
unsigned long dev_alignment_offset ( struct dev_types * dt , struct device * dev )
2009-08-01 21:07:36 +04:00
{
return 0UL ;
}
2013-06-12 14:08:56 +04:00
unsigned long dev_minimum_io_size ( struct dev_types * dt , struct device * dev )
2009-08-01 21:08:43 +04:00
{
return 0UL ;
}
2013-06-12 14:08:56 +04:00
unsigned long dev_optimal_io_size ( struct dev_types * dt , struct device * dev )
2009-08-01 21:08:43 +04:00
{
return 0UL ;
}
2013-06-12 14:08:56 +04:00
unsigned long dev_discard_max_bytes ( struct dev_types * dt , struct device * dev )
2011-04-13 01:59:01 +04:00
{
return 0UL ;
}
2013-06-12 14:08:56 +04:00
unsigned long dev_discard_granularity ( struct dev_types * dt , struct device * dev )
2011-04-13 01:59:01 +04:00
{
return 0UL ;
}
2014-09-16 03:44:25 +04:00
int dev_is_rotational ( struct dev_types * dt , struct device * dev )
{
return 1 ;
}
2009-08-01 21:07:36 +04:00
# endif