2005-04-17 02:20:36 +04:00
/*
* fs / partitions / msdos . c
*
* Code extracted from drivers / block / genhd . c
* Copyright ( C ) 1991 - 1998 Linus Torvalds
*
* Thanks to Branko Lankester , lankeste @ fwi . uva . nl , who found a bug
* in the early extended - partition checks and added DM partitions
*
* Support for DiskManager v6 .0 x added by Mark Lord ,
* with information provided by OnTrack . This now works for linux fdisk
* and LILO , as well as loadlin and bootln . Note that disks other than
* / dev / hda * must * have a " DOS " type 0x51 partition in the first slot ( hda1 ) .
*
* More flexible handling of extended partitions - aeb , 950831
*
* Check partition table on IDE disks for common CHS translations
*
* Re - organised Feb 1998 Russell King
*/
# include "check.h"
# include "msdos.h"
# include "efi.h"
/*
* Many architectures don ' t like unaligned accesses , while
* the nr_sects and start_sect partition table entries are
* at a 2 ( mod 4 ) address .
*/
# include <asm/unaligned.h>
# define SYS_IND(p) (get_unaligned(&p->sys_ind))
# define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
get_unaligned ( & p - > nr_sects ) ; \
le32_to_cpu ( __a ) ; \
} )
# define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
get_unaligned ( & p - > start_sect ) ; \
le32_to_cpu ( __a ) ; \
} )
static inline int is_extended_partition ( struct partition * p )
{
return ( SYS_IND ( p ) = = DOS_EXTENDED_PARTITION | |
SYS_IND ( p ) = = WIN98_EXTENDED_PARTITION | |
SYS_IND ( p ) = = LINUX_EXTENDED_PARTITION ) ;
}
# define MSDOS_LABEL_MAGIC1 0x55
# define MSDOS_LABEL_MAGIC2 0xAA
static inline int
msdos_magic_present ( unsigned char * p )
{
return ( p [ 0 ] = = MSDOS_LABEL_MAGIC1 & & p [ 1 ] = = MSDOS_LABEL_MAGIC2 ) ;
}
/*
* Create devices for each logical partition in an extended partition .
* The logical partitions form a linked list , with each entry being
* a partition table with two entries . The first entry
* is the real data partition ( with a start relative to the partition
* table start ) . The second is a pointer to the next logical partition
* ( with a start relative to the entire extended partition ) .
* We do not create a Linux partition for the partition tables , but
* only for the actual data partitions .
*/
static void
parse_extended ( struct parsed_partitions * state , struct block_device * bdev ,
u32 first_sector , u32 first_size )
{
struct partition * p ;
Sector sect ;
unsigned char * data ;
u32 this_sector , this_size ;
int sector_size = bdev_hardsect_size ( bdev ) / 512 ;
int loopct = 0 ; /* number of links followed
without finding a data partition */
int i ;
this_sector = first_sector ;
this_size = first_size ;
while ( 1 ) {
if ( + + loopct > 100 )
return ;
if ( state - > next = = state - > limit )
return ;
data = read_dev_sector ( bdev , this_sector , & sect ) ;
if ( ! data )
return ;
if ( ! msdos_magic_present ( data + 510 ) )
goto done ;
p = ( struct partition * ) ( data + 0x1be ) ;
/*
* Usually , the first entry is the real data partition ,
* the 2 nd entry is the next extended partition , or empty ,
* and the 3 rd and 4 th entries are unused .
* However , DRDOS sometimes has the extended partition as
* the first entry ( when the data partition is empty ) ,
* and OS / 2 seems to use all four entries .
*/
/*
* First process the data partition ( s )
*/
for ( i = 0 ; i < 4 ; i + + , p + + ) {
u32 offs , size , next ;
if ( ! NR_SECTS ( p ) | | is_extended_partition ( p ) )
continue ;
/* Check the 3rd and 4th entries -
these sometimes contain random garbage */
offs = START_SECT ( p ) * sector_size ;
size = NR_SECTS ( p ) * sector_size ;
next = this_sector + offs ;
if ( i > = 2 ) {
if ( offs + size > this_size )
continue ;
if ( next < first_sector )
continue ;
if ( next + size > first_sector + first_size )
continue ;
}
put_partition ( state , state - > next , next , size ) ;
if ( SYS_IND ( p ) = = LINUX_RAID_PARTITION )
state - > parts [ state - > next ] . flags = 1 ;
loopct = 0 ;
if ( + + state - > next = = state - > limit )
goto done ;
}
/*
* Next , process the ( first ) extended partition , if present .
* ( So far , there seems to be no reason to make
* parse_extended ( ) recursive and allow a tree
* of extended partitions . )
* It should be a link to the next logical partition .
*/
p - = 4 ;
for ( i = 0 ; i < 4 ; i + + , p + + )
if ( NR_SECTS ( p ) & & is_extended_partition ( p ) )
break ;
if ( i = = 4 )
goto done ; /* nothing left to do */
this_sector = first_sector + START_SECT ( p ) * sector_size ;
this_size = NR_SECTS ( p ) * sector_size ;
put_dev_sector ( sect ) ;
}
done :
put_dev_sector ( sect ) ;
}
/* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
indicates linux swap . Be careful before believing this is Solaris . */
static void
parse_solaris_x86 ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_SOLARIS_X86_PARTITION
Sector sect ;
struct solaris_x86_vtoc * v ;
int i ;
v = ( struct solaris_x86_vtoc * ) read_dev_sector ( bdev , offset + 1 , & sect ) ;
if ( ! v )
return ;
if ( le32_to_cpu ( v - > v_sanity ) ! = SOLARIS_X86_VTOC_SANE ) {
put_dev_sector ( sect ) ;
return ;
}
printk ( " %s%d: <solaris: " , state - > name , origin ) ;
if ( le32_to_cpu ( v - > v_version ) ! = 1 ) {
printk ( " cannot handle version %d vtoc> \n " ,
le32_to_cpu ( v - > v_version ) ) ;
put_dev_sector ( sect ) ;
return ;
}
for ( i = 0 ; i < SOLARIS_X86_NUMSLICE & & state - > next < state - > limit ; i + + ) {
struct solaris_x86_slice * s = & v - > v_slice [ i ] ;
if ( s - > s_size = = 0 )
continue ;
printk ( " [s%d] " , i ) ;
/* solaris partitions are relative to current MS-DOS
* one ; must add the offset of the current partition */
put_partition ( state , state - > next + + ,
le32_to_cpu ( s - > s_start ) + offset ,
le32_to_cpu ( s - > s_size ) ) ;
}
put_dev_sector ( sect ) ;
printk ( " > \n " ) ;
# endif
}
2005-06-26 01:58:47 +04:00
# if defined(CONFIG_BSD_DISKLABEL)
2005-04-17 02:20:36 +04:00
/*
* Create devices for BSD partitions listed in a disklabel , under a
* dos - like partition . See parse_extended ( ) for more information .
*/
2005-06-26 01:58:47 +04:00
static void
2005-04-17 02:20:36 +04:00
parse_bsd ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin , char * flavour ,
int max_partitions )
{
Sector sect ;
struct bsd_disklabel * l ;
struct bsd_partition * p ;
l = ( struct bsd_disklabel * ) read_dev_sector ( bdev , offset + 1 , & sect ) ;
if ( ! l )
return ;
if ( le32_to_cpu ( l - > d_magic ) ! = BSD_DISKMAGIC ) {
put_dev_sector ( sect ) ;
return ;
}
printk ( " %s%d: <%s: " , state - > name , origin , flavour ) ;
if ( le16_to_cpu ( l - > d_npartitions ) < max_partitions )
max_partitions = le16_to_cpu ( l - > d_npartitions ) ;
for ( p = l - > d_partitions ; p - l - > d_partitions < max_partitions ; p + + ) {
u32 bsd_start , bsd_size ;
if ( state - > next = = state - > limit )
break ;
if ( p - > p_fstype = = BSD_FS_UNUSED )
continue ;
bsd_start = le32_to_cpu ( p - > p_offset ) ;
bsd_size = le32_to_cpu ( p - > p_size ) ;
if ( offset = = bsd_start & & size = = bsd_size )
/* full parent partition, we have it already */
continue ;
if ( offset > bsd_start | | offset + size < bsd_start + bsd_size ) {
printk ( " bad subpartition - ignored \n " ) ;
continue ;
}
put_partition ( state , state - > next + + , bsd_start , bsd_size ) ;
}
put_dev_sector ( sect ) ;
if ( le16_to_cpu ( l - > d_npartitions ) > max_partitions )
printk ( " (ignored %d more) " ,
le16_to_cpu ( l - > d_npartitions ) - max_partitions ) ;
printk ( " > \n " ) ;
}
# endif
static void
parse_freebsd ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_BSD_DISKLABEL
parse_bsd ( state , bdev , offset , size , origin ,
" bsd " , BSD_MAXPARTITIONS ) ;
# endif
}
static void
parse_netbsd ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_BSD_DISKLABEL
parse_bsd ( state , bdev , offset , size , origin ,
" netbsd " , BSD_MAXPARTITIONS ) ;
# endif
}
static void
parse_openbsd ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_BSD_DISKLABEL
parse_bsd ( state , bdev , offset , size , origin ,
" openbsd " , OPENBSD_MAXPARTITIONS ) ;
# endif
}
/*
* Create devices for Unixware partitions listed in a disklabel , under a
* dos - like partition . See parse_extended ( ) for more information .
*/
static void
parse_unixware ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_UNIXWARE_DISKLABEL
Sector sect ;
struct unixware_disklabel * l ;
struct unixware_slice * p ;
l = ( struct unixware_disklabel * ) read_dev_sector ( bdev , offset + 29 , & sect ) ;
if ( ! l )
return ;
if ( le32_to_cpu ( l - > d_magic ) ! = UNIXWARE_DISKMAGIC | |
le32_to_cpu ( l - > vtoc . v_magic ) ! = UNIXWARE_DISKMAGIC2 ) {
put_dev_sector ( sect ) ;
return ;
}
printk ( " %s%d: <unixware: " , state - > name , origin ) ;
p = & l - > vtoc . v_slice [ 1 ] ;
/* I omit the 0th slice as it is the same as whole disk. */
while ( p - & l - > vtoc . v_slice [ 0 ] < UNIXWARE_NUMSLICE ) {
if ( state - > next = = state - > limit )
break ;
if ( p - > s_label ! = UNIXWARE_FS_UNUSED )
put_partition ( state , state - > next + + ,
START_SECT ( p ) , NR_SECTS ( p ) ) ;
p + + ;
}
put_dev_sector ( sect ) ;
printk ( " > \n " ) ;
# endif
}
/*
* Minix 2.0 .0 / 2.0 .2 subpartition support .
* Anand Krishnamurthy < anandk @ wiproge . med . ge . com >
* Rajeev V . Pillai < rajeevvp @ yahoo . com >
*/
static void
parse_minix ( struct parsed_partitions * state , struct block_device * bdev ,
u32 offset , u32 size , int origin )
{
# ifdef CONFIG_MINIX_SUBPARTITION
Sector sect ;
unsigned char * data ;
struct partition * p ;
int i ;
data = read_dev_sector ( bdev , offset , & sect ) ;
if ( ! data )
return ;
p = ( struct partition * ) ( data + 0x1be ) ;
/* The first sector of a Minix partition can have either
* a secondary MBR describing its subpartitions , or
* the normal boot sector . */
if ( msdos_magic_present ( data + 510 ) & &
SYS_IND ( p ) = = MINIX_PARTITION ) { /* subpartition table present */
printk ( " %s%d: <minix: " , state - > name , origin ) ;
for ( i = 0 ; i < MINIX_NR_SUBPARTITIONS ; i + + , p + + ) {
if ( state - > next = = state - > limit )
break ;
/* add each partition in use */
if ( SYS_IND ( p ) = = MINIX_PARTITION )
put_partition ( state , state - > next + + ,
START_SECT ( p ) , NR_SECTS ( p ) ) ;
}
printk ( " > \n " ) ;
}
put_dev_sector ( sect ) ;
# endif /* CONFIG_MINIX_SUBPARTITION */
}
static struct {
unsigned char id ;
void ( * parse ) ( struct parsed_partitions * , struct block_device * ,
u32 , u32 , int ) ;
} subtypes [ ] = {
{ FREEBSD_PARTITION , parse_freebsd } ,
{ NETBSD_PARTITION , parse_netbsd } ,
{ OPENBSD_PARTITION , parse_openbsd } ,
{ MINIX_PARTITION , parse_minix } ,
{ UNIXWARE_PARTITION , parse_unixware } ,
{ SOLARIS_X86_PARTITION , parse_solaris_x86 } ,
{ NEW_SOLARIS_X86_PARTITION , parse_solaris_x86 } ,
{ 0 , NULL } ,
} ;
int msdos_partition ( struct parsed_partitions * state , struct block_device * bdev )
{
int sector_size = bdev_hardsect_size ( bdev ) / 512 ;
Sector sect ;
unsigned char * data ;
struct partition * p ;
int slot ;
data = read_dev_sector ( bdev , 0 , & sect ) ;
if ( ! data )
return - 1 ;
if ( ! msdos_magic_present ( data + 510 ) ) {
put_dev_sector ( sect ) ;
return 0 ;
}
/*
* Now that the 55 aa signature is present , this is probably
* either the boot sector of a FAT filesystem or a DOS - type
* partition table . Reject this in case the boot indicator
* is not 0 or 0x80 .
*/
p = ( struct partition * ) ( data + 0x1be ) ;
for ( slot = 1 ; slot < = 4 ; slot + + , p + + ) {
if ( p - > boot_ind ! = 0 & & p - > boot_ind ! = 0x80 ) {
put_dev_sector ( sect ) ;
return 0 ;
}
}
# ifdef CONFIG_EFI_PARTITION
p = ( struct partition * ) ( data + 0x1be ) ;
for ( slot = 1 ; slot < = 4 ; slot + + , p + + ) {
/* If this is an EFI GPT disk, msdos should ignore it. */
if ( SYS_IND ( p ) = = EFI_PMBR_OSTYPE_EFI_GPT ) {
put_dev_sector ( sect ) ;
return 0 ;
}
}
# endif
p = ( struct partition * ) ( data + 0x1be ) ;
/*
* Look for partitions in two passes :
* First find the primary and DOS - type extended partitions .
* On the second pass look inside * BSD , Unixware and Solaris partitions .
*/
state - > next = 5 ;
for ( slot = 1 ; slot < = 4 ; slot + + , p + + ) {
u32 start = START_SECT ( p ) * sector_size ;
u32 size = NR_SECTS ( p ) * sector_size ;
if ( ! size )
continue ;
if ( is_extended_partition ( p ) ) {
/* prevent someone doing mkfs or mkswap on an
extended partition , but leave room for LILO */
put_partition ( state , slot , start , size = = 1 ? 1 : 2 ) ;
printk ( " < " ) ;
parse_extended ( state , bdev , start , size ) ;
printk ( " > " ) ;
continue ;
}
put_partition ( state , slot , start , size ) ;
if ( SYS_IND ( p ) = = LINUX_RAID_PARTITION )
state - > parts [ slot ] . flags = 1 ;
if ( SYS_IND ( p ) = = DM6_PARTITION )
printk ( " [DM] " ) ;
if ( SYS_IND ( p ) = = EZD_PARTITION )
printk ( " [EZD] " ) ;
}
printk ( " \n " ) ;
/* second pass - output for each on a separate line */
p = ( struct partition * ) ( 0x1be + data ) ;
for ( slot = 1 ; slot < = 4 ; slot + + , p + + ) {
unsigned char id = SYS_IND ( p ) ;
int n ;
if ( ! NR_SECTS ( p ) )
continue ;
for ( n = 0 ; subtypes [ n ] . parse & & id ! = subtypes [ n ] . id ; n + + )
;
if ( ! subtypes [ n ] . parse )
continue ;
subtypes [ n ] . parse ( state , bdev , START_SECT ( p ) * sector_size ,
NR_SECTS ( p ) * sector_size , slot ) ;
}
put_dev_sector ( sect ) ;
return 1 ;
}