2005-01-31 07:28:44 +03:00
/*
* volume_id - reads filesystem label and uuid
*
* Copyright ( C ) 2004 Kay Sievers < kay . sievers @ vrfy . org >
*
2005-09-27 18:27:35 +04:00
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License .
2005-01-31 07:28:44 +03:00
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include <errno.h>
# include <ctype.h>
2005-02-23 04:58:31 +03:00
# include "volume_id.h"
# include "logging.h"
# include "util.h"
2005-01-31 07:28:44 +03:00
2005-02-09 03:02:18 +03:00
struct msdos_partition_entry {
2005-08-01 03:33:36 +04:00
uint8_t boot_ind ;
uint8_t head ;
uint8_t sector ;
uint8_t cyl ;
uint8_t sys_ind ;
uint8_t end_head ;
uint8_t end_sector ;
uint8_t end_cyl ;
uint32_t start_sect ;
uint32_t nr_sects ;
2005-02-09 03:02:18 +03:00
} __attribute__ ( ( packed ) ) ;
2005-01-31 07:28:44 +03:00
# define MSDOS_MAGIC "\x55\xaa"
# define MSDOS_PARTTABLE_OFFSET 0x1be
# define MSDOS_SIG_OFF 0x1fe
# define BSIZE 0x200
# define DOS_EXTENDED_PARTITION 0x05
# define LINUX_EXTENDED_PARTITION 0x85
# define WIN98_EXTENDED_PARTITION 0x0f
# define LINUX_RAID_PARTITION 0xfd
# define is_extended(type) \
( type = = DOS_EXTENDED_PARTITION | | \
type = = WIN98_EXTENDED_PARTITION | | \
type = = LINUX_EXTENDED_PARTITION )
# define is_raid(type) \
( type = = LINUX_RAID_PARTITION )
2005-08-01 03:33:36 +04:00
int volume_id_probe_msdos_part_table ( struct volume_id * id , uint64_t off )
2005-01-31 07:28:44 +03:00
{
2005-08-01 03:33:36 +04:00
const uint8_t * buf ;
2005-01-31 07:28:44 +03:00
int i ;
2005-08-01 03:33:36 +04:00
uint64_t poff ;
uint64_t plen ;
uint64_t extended = 0 ;
uint64_t current ;
uint64_t next ;
2005-01-31 07:28:44 +03:00
int limit ;
int empty = 1 ;
2005-02-09 03:02:18 +03:00
struct msdos_partition_entry * part ;
2005-01-31 07:28:44 +03:00
struct volume_id_partition * p ;
2005-03-10 02:58:01 +03:00
dbg ( " probing at offset 0x%llx " , ( unsigned long long ) off ) ;
2005-02-09 03:02:18 +03:00
2005-01-31 07:28:44 +03:00
buf = volume_id_get_buffer ( id , off , 0x200 ) ;
if ( buf = = NULL )
return - 1 ;
2005-02-05 06:10:48 +03:00
if ( memcmp ( & buf [ MSDOS_SIG_OFF ] , MSDOS_MAGIC , 2 ) ! = 0 )
2005-01-31 07:28:44 +03:00
return - 1 ;
/* check flags on all entries for a valid partition table */
part = ( struct msdos_partition_entry * ) & buf [ MSDOS_PARTTABLE_OFFSET ] ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( part [ i ] . boot_ind ! = 0 & &
part [ i ] . boot_ind ! = 0x80 )
return - 1 ;
if ( le32_to_cpu ( part [ i ] . nr_sects ) ! = 0 )
empty = 0 ;
}
if ( empty = = 1 )
return - 1 ;
if ( id - > partitions ! = NULL )
free ( id - > partitions ) ;
id - > partitions = malloc ( VOLUME_ID_PARTITIONS_MAX *
sizeof ( struct volume_id_partition ) ) ;
if ( id - > partitions = = NULL )
return - 1 ;
memset ( id - > partitions , 0x00 ,
VOLUME_ID_PARTITIONS_MAX * sizeof ( struct volume_id_partition ) ) ;
for ( i = 0 ; i < 4 ; i + + ) {
2005-08-01 03:33:36 +04:00
poff = ( uint64_t ) le32_to_cpu ( part [ i ] . start_sect ) * BSIZE ;
plen = ( uint64_t ) le32_to_cpu ( part [ i ] . nr_sects ) * BSIZE ;
2005-01-31 07:28:44 +03:00
if ( plen = = 0 )
continue ;
p = & id - > partitions [ i ] ;
p - > partition_type_raw = part [ i ] . sys_ind ;
if ( is_extended ( part [ i ] . sys_ind ) ) {
2005-03-10 02:58:01 +03:00
dbg ( " found extended partition at 0x%llx " , ( unsigned long long ) poff ) ;
2005-01-31 07:28:44 +03:00
volume_id_set_usage_part ( p , VOLUME_ID_PARTITIONTABLE ) ;
p - > type = " msdos_extended_partition " ;
if ( extended = = 0 )
extended = off + poff ;
} else {
dbg ( " found 0x%x data partition at 0x%llx, len 0x%llx " ,
2005-03-10 02:58:01 +03:00
part [ i ] . sys_ind , ( unsigned long long ) poff , ( unsigned long long ) plen ) ;
2005-01-31 07:28:44 +03:00
if ( is_raid ( part [ i ] . sys_ind ) )
volume_id_set_usage_part ( p , VOLUME_ID_RAID ) ;
else
volume_id_set_usage_part ( p , VOLUME_ID_UNPROBED ) ;
}
p - > off = off + poff ;
p - > len = plen ;
id - > partition_count = i + 1 ;
}
next = extended ;
current = extended ;
limit = 50 ;
/* follow extended partition chain and add data partitions */
while ( next ! = 0 ) {
if ( limit - - = = 0 ) {
dbg ( " extended chain limit reached " ) ;
break ;
}
buf = volume_id_get_buffer ( id , current , 0x200 ) ;
if ( buf = = NULL )
break ;
part = ( struct msdos_partition_entry * ) & buf [ MSDOS_PARTTABLE_OFFSET ] ;
2005-02-05 06:10:48 +03:00
if ( memcmp ( & buf [ MSDOS_SIG_OFF ] , MSDOS_MAGIC , 2 ) ! = 0 )
2005-01-31 07:28:44 +03:00
break ;
next = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
2005-08-01 03:33:36 +04:00
poff = ( uint64_t ) le32_to_cpu ( part [ i ] . start_sect ) * BSIZE ;
plen = ( uint64_t ) le32_to_cpu ( part [ i ] . nr_sects ) * BSIZE ;
2005-01-31 07:28:44 +03:00
if ( plen = = 0 )
continue ;
if ( is_extended ( part [ i ] . sys_ind ) ) {
2005-03-10 02:58:01 +03:00
dbg ( " found extended partition at 0x%llx " , ( unsigned long long ) poff ) ;
2005-01-31 07:28:44 +03:00
if ( next = = 0 )
next = extended + poff ;
} else {
dbg ( " found 0x%x data partition at 0x%llx, len 0x%llx " ,
2005-03-10 02:58:01 +03:00
part [ i ] . sys_ind , ( unsigned long long ) poff , ( unsigned long long ) plen ) ;
2005-01-31 07:28:44 +03:00
/* we always start at the 5th entry */
while ( id - > partition_count < 4 )
volume_id_set_usage_part ( & id - > partitions [ id - > partition_count + + ] , VOLUME_ID_UNUSED ) ;
p = & id - > partitions [ id - > partition_count ] ;
if ( is_raid ( part [ i ] . sys_ind ) )
volume_id_set_usage_part ( p , VOLUME_ID_RAID ) ;
else
volume_id_set_usage_part ( p , VOLUME_ID_UNPROBED ) ;
p - > off = current + poff ;
p - > len = plen ;
id - > partition_count + + ;
p - > partition_type_raw = part [ i ] . sys_ind ;
if ( id - > partition_count > = VOLUME_ID_PARTITIONS_MAX ) {
dbg ( " too many partitions " ) ;
next = 0 ;
}
}
}
current = next ;
}
volume_id_set_usage ( id , VOLUME_ID_PARTITIONTABLE ) ;
id - > type = " msdos_partition_table " ;
return 0 ;
}