2019-04-30 21:42:40 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2021-08-05 20:34:47 +03:00
/*
2005-04-17 02:20:36 +04:00
* ldm - Support for Windows Logical Disk Manager ( Dynamic Disks )
*
* Copyright ( C ) 2001 , 2002 Richard Russon < ldm @ flatcap . org >
2012-02-24 13:37:42 +04:00
* Copyright ( c ) 2001 - 2012 Anton Altaparmakov
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2001 , 2002 Jakob Kemi < jakob . kemi @ telia . com >
*
2010-10-18 13:03:14 +04:00
* Documentation is available at http : //www.linux-ntfs.org/doku.php?id=downloads
2005-04-17 02:20:36 +04:00
*/
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <linux/stringify.h>
2010-05-25 01:33:27 +04:00
# include <linux/kernel.h>
2016-05-21 03:01:27 +03:00
# include <linux/uuid.h>
2020-03-24 10:25:26 +03:00
# include <linux/msdos_partition.h>
2016-05-21 03:01:27 +03:00
2005-04-17 02:20:36 +04:00
# include "ldm.h"
# include "check.h"
2019-05-31 03:00:46 +03:00
/*
2005-04-17 02:20:36 +04:00
* ldm_debug / info / error / crit - Output an error message
* @ f : A printf format string containing the message
* @ . . . : Variables to substitute into @ f
*
* ldm_debug ( ) writes a DEBUG level message to the syslog but only if the
* driver was compiled with debug enabled . Otherwise , the call turns into a NOP .
*/
# ifndef CONFIG_LDM_DEBUG
# define ldm_debug(...) do {} while (0)
# else
2008-04-30 11:55:09 +04:00
# define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __func__, f, ##a)
2005-04-17 02:20:36 +04:00
# endif
2008-04-30 11:55:09 +04:00
# define ldm_crit(f, a...) _ldm_printk (KERN_CRIT, __func__, f, ##a)
# define ldm_error(f, a...) _ldm_printk (KERN_ERR, __func__, f, ##a)
# define ldm_info(f, a...) _ldm_printk (KERN_INFO, __func__, f, ##a)
2005-04-17 02:20:36 +04:00
2011-11-01 04:11:33 +04:00
static __printf ( 3 , 4 )
void _ldm_printk ( const char * level , const char * function , const char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
2011-11-01 04:11:33 +04:00
struct va_format vaf ;
2005-04-17 02:20:36 +04:00
va_list args ;
va_start ( args , fmt ) ;
2011-11-01 04:11:33 +04:00
vaf . fmt = fmt ;
vaf . va = & args ;
printk ( " %s%s(): %pV \n " , level , function , & vaf ) ;
va_end ( args ) ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
* @ data : Raw database PRIVHEAD structure loaded from the device
* @ ph : In - memory privhead structure in which to return parsed information
*
* This parses the LDM database PRIVHEAD structure supplied in @ data and
* sets up the in - memory privhead structure @ ph with the obtained information .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ ph contains the PRIVHEAD data
* ' false ' @ ph contents are undefined
2005-04-17 02:20:36 +04:00
*/
2007-05-21 12:37:42 +04:00
static bool ldm_parse_privhead ( const u8 * data , struct privhead * ph )
2005-04-17 02:20:36 +04:00
{
2007-05-21 12:37:42 +04:00
bool is_vista = false ;
2005-04-17 02:20:36 +04:00
2007-05-21 12:37:42 +04:00
BUG_ON ( ! data | | ! ph ) ;
2008-07-25 12:45:25 +04:00
if ( MAGIC_PRIVHEAD ! = get_unaligned_be64 ( data ) ) {
2007-05-21 12:37:42 +04:00
ldm_error ( " Cannot find PRIVHEAD structure. LDM database is "
2005-04-17 02:20:36 +04:00
" corrupt. Aborting. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 12:45:25 +04:00
ph - > ver_major = get_unaligned_be16 ( data + 0x000C ) ;
ph - > ver_minor = get_unaligned_be16 ( data + 0x000E ) ;
ph - > logical_disk_start = get_unaligned_be64 ( data + 0x011B ) ;
ph - > logical_disk_size = get_unaligned_be64 ( data + 0x0123 ) ;
ph - > config_start = get_unaligned_be64 ( data + 0x012B ) ;
ph - > config_size = get_unaligned_be64 ( data + 0x0133 ) ;
2007-05-21 12:37:42 +04:00
/* Version 2.11 is Win2k/XP and version 2.12 is Vista. */
if ( ph - > ver_major = = 2 & & ph - > ver_minor = = 12 )
is_vista = true ;
if ( ! is_vista & & ( ph - > ver_major ! = 2 | | ph - > ver_minor ! = 11 ) ) {
ldm_error ( " Expected PRIVHEAD version 2.11 or 2.12, got %d.%d. "
" Aborting. " , ph - > ver_major , ph - > ver_minor ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
ldm_debug ( " PRIVHEAD version %d.%d (Windows %s). " , ph - > ver_major ,
ph - > ver_minor , is_vista ? " Vista " : " 2000/XP " ) ;
2005-04-17 02:20:36 +04:00
if ( ph - > config_size ! = LDM_DB_SIZE ) { /* 1 MiB in sectors. */
2007-05-21 12:37:42 +04:00
/* Warn the user and continue, carefully. */
ldm_info ( " Database is normally %u bytes, it claims to "
2005-04-17 02:20:36 +04:00
" be %llu bytes. " , LDM_DB_SIZE ,
2007-05-22 08:28:11 +04:00
( unsigned long long ) ph - > config_size ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
if ( ( ph - > logical_disk_size = = 0 ) | | ( ph - > logical_disk_start +
ph - > logical_disk_size > ph - > config_start ) ) {
ldm_error ( " PRIVHEAD disk size doesn't match real disk size " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2017-05-17 10:38:37 +03:00
if ( uuid_parse ( data + 0x0030 , & ph - > disk_id ) ) {
2007-05-21 12:37:42 +04:00
ldm_error ( " PRIVHEAD contains an invalid GUID. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
ldm_debug ( " Parsed PRIVHEAD successfully. " ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure
* @ data : Raw database TOCBLOCK structure loaded from the device
* @ toc : In - memory toc structure in which to return parsed information
*
* This parses the LDM Database TOCBLOCK ( table of contents ) structure supplied
* in @ data and sets up the in - memory tocblock structure @ toc with the obtained
* information .
*
* N . B . The * _start and * _size values returned in @ toc are not range - checked .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ toc contains the TOCBLOCK data
* ' false ' @ toc contents are undefined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_tocblock ( const u8 * data , struct tocblock * toc )
2005-04-17 02:20:36 +04:00
{
BUG_ON ( ! data | | ! toc ) ;
2008-07-25 12:45:25 +04:00
if ( MAGIC_TOCBLOCK ! = get_unaligned_be64 ( data ) ) {
2005-04-17 02:20:36 +04:00
ldm_crit ( " Cannot find TOCBLOCK, database may be corrupt. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
strncpy ( toc - > bitmap1_name , data + 0x24 , sizeof ( toc - > bitmap1_name ) ) ;
toc - > bitmap1_name [ sizeof ( toc - > bitmap1_name ) - 1 ] = 0 ;
2008-07-25 12:45:25 +04:00
toc - > bitmap1_start = get_unaligned_be64 ( data + 0x2E ) ;
toc - > bitmap1_size = get_unaligned_be64 ( data + 0x36 ) ;
2005-04-17 02:20:36 +04:00
if ( strncmp ( toc - > bitmap1_name , TOC_BITMAP1 ,
sizeof ( toc - > bitmap1_name ) ) ! = 0 ) {
ldm_crit ( " TOCBLOCK's first bitmap is '%s', should be '%s'. " ,
TOC_BITMAP1 , toc - > bitmap1_name ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
strncpy ( toc - > bitmap2_name , data + 0x46 , sizeof ( toc - > bitmap2_name ) ) ;
toc - > bitmap2_name [ sizeof ( toc - > bitmap2_name ) - 1 ] = 0 ;
2008-07-25 12:45:25 +04:00
toc - > bitmap2_start = get_unaligned_be64 ( data + 0x50 ) ;
toc - > bitmap2_size = get_unaligned_be64 ( data + 0x58 ) ;
2005-04-17 02:20:36 +04:00
if ( strncmp ( toc - > bitmap2_name , TOC_BITMAP2 ,
sizeof ( toc - > bitmap2_name ) ) ! = 0 ) {
ldm_crit ( " TOCBLOCK's second bitmap is '%s', should be '%s'. " ,
TOC_BITMAP2 , toc - > bitmap2_name ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
ldm_debug ( " Parsed TOCBLOCK successfully. " ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_vmdb - Read the LDM Database VMDB structure
* @ data : Raw database VMDB structure loaded from the device
* @ vm : In - memory vmdb structure in which to return parsed information
*
* This parses the LDM Database VMDB structure supplied in @ data and sets up
* the in - memory vmdb structure @ vm with the obtained information .
*
* N . B . The * _start , * _size and * _seq values will be range - checked later .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vm contains VMDB info
* ' false ' @ vm contents are undefined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_vmdb ( const u8 * data , struct vmdb * vm )
2005-04-17 02:20:36 +04:00
{
BUG_ON ( ! data | | ! vm ) ;
2008-07-25 12:45:25 +04:00
if ( MAGIC_VMDB ! = get_unaligned_be32 ( data ) ) {
2005-04-17 02:20:36 +04:00
ldm_crit ( " Cannot find the VMDB, database may be corrupt. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 12:45:25 +04:00
vm - > ver_major = get_unaligned_be16 ( data + 0x12 ) ;
vm - > ver_minor = get_unaligned_be16 ( data + 0x14 ) ;
2005-04-17 02:20:36 +04:00
if ( ( vm - > ver_major ! = 4 ) | | ( vm - > ver_minor ! = 10 ) ) {
ldm_error ( " Expected VMDB version %d.%d, got %d.%d. "
" Aborting. " , 4 , 10 , vm - > ver_major , vm - > ver_minor ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 12:45:25 +04:00
vm - > vblk_size = get_unaligned_be32 ( data + 0x08 ) ;
2011-02-26 01:44:21 +03:00
if ( vm - > vblk_size = = 0 ) {
ldm_error ( " Illegal VBLK size " ) ;
return false ;
}
2008-07-25 12:45:25 +04:00
vm - > vblk_offset = get_unaligned_be32 ( data + 0x0C ) ;
vm - > last_vblk_seq = get_unaligned_be32 ( data + 0x04 ) ;
2005-04-17 02:20:36 +04:00
ldm_debug ( " Parsed VMDB successfully. " ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_compare_privheads - Compare two privhead objects
* @ ph1 : First privhead
* @ ph2 : Second privhead
*
* This compares the two privhead structures @ ph1 and @ ph2 .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' Identical
* ' false ' Different
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_compare_privheads ( const struct privhead * ph1 ,
2005-04-17 02:20:36 +04:00
const struct privhead * ph2 )
{
BUG_ON ( ! ph1 | | ! ph2 ) ;
return ( ( ph1 - > ver_major = = ph2 - > ver_major ) & &
( ph1 - > ver_minor = = ph2 - > ver_minor ) & &
( ph1 - > logical_disk_start = = ph2 - > logical_disk_start ) & &
( ph1 - > logical_disk_size = = ph2 - > logical_disk_size ) & &
( ph1 - > config_start = = ph2 - > config_start ) & &
( ph1 - > config_size = = ph2 - > config_size ) & &
2017-05-17 10:38:37 +03:00
uuid_equal ( & ph1 - > disk_id , & ph2 - > disk_id ) ) ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_compare_tocblocks - Compare two tocblock objects
* @ toc1 : First toc
* @ toc2 : Second toc
*
* This compares the two tocblock structures @ toc1 and @ toc2 .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' Identical
* ' false ' Different
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_compare_tocblocks ( const struct tocblock * toc1 ,
2005-04-17 02:20:36 +04:00
const struct tocblock * toc2 )
{
BUG_ON ( ! toc1 | | ! toc2 ) ;
return ( ( toc1 - > bitmap1_start = = toc2 - > bitmap1_start ) & &
( toc1 - > bitmap1_size = = toc2 - > bitmap1_size ) & &
( toc1 - > bitmap2_start = = toc2 - > bitmap2_start ) & &
( toc1 - > bitmap2_size = = toc2 - > bitmap2_size ) & &
! strncmp ( toc1 - > bitmap1_name , toc2 - > bitmap1_name ,
sizeof ( toc1 - > bitmap1_name ) ) & &
! strncmp ( toc1 - > bitmap2_name , toc2 - > bitmap2_name ,
sizeof ( toc1 - > bitmap2_name ) ) ) ;
}
/**
* ldm_validate_privheads - Compare the primary privhead with its backups
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2005-04-17 02:20:36 +04:00
* @ ph1 : Memory struct to fill with ph contents
*
* Read and compare all three privheads from disk .
*
* The privheads on disk show the size and location of the main disk area and
* the configuration area ( the database ) . The values are range - checked against
* @ hd , which contains the real size of the disk .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' Success
* ' false ' Error
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
static bool ldm_validate_privheads ( struct parsed_partitions * state ,
struct privhead * ph1 )
2005-04-17 02:20:36 +04:00
{
static const int off [ 3 ] = { OFF_PRIV1 , OFF_PRIV2 , OFF_PRIV3 } ;
struct privhead * ph [ 3 ] = { ph1 } ;
Sector sect ;
u8 * data ;
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
long num_sects ;
int i ;
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state | | ! ph1 ) ;
2005-04-17 02:20:36 +04:00
ph [ 1 ] = kmalloc ( sizeof ( * ph [ 1 ] ) , GFP_KERNEL ) ;
ph [ 2 ] = kmalloc ( sizeof ( * ph [ 2 ] ) , GFP_KERNEL ) ;
if ( ! ph [ 1 ] | | ! ph [ 2 ] ) {
ldm_crit ( " Out of memory. " ) ;
goto out ;
}
/* off[1 & 2] are relative to ph[0]->config_start */
ph [ 0 ] - > config_start = 0 ;
/* Read and parse privheads */
for ( i = 0 ; i < 3 ; i + + ) {
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , ph [ 0 ] - > config_start + off [ i ] ,
& sect ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
ldm_crit ( " Disk read failed. " ) ;
goto out ;
}
result = ldm_parse_privhead ( data , ph [ i ] ) ;
put_dev_sector ( sect ) ;
if ( ! result ) {
ldm_error ( " Cannot find PRIVHEAD %d. " , i + 1 ) ; /* Log again */
if ( i < 2 )
goto out ; /* Already logged */
else
break ; /* FIXME ignore for now, 3rd PH can fail on odd-sized disks */
}
}
2021-08-10 18:45:09 +03:00
num_sects = get_capacity ( state - > disk ) ;
2005-04-17 02:20:36 +04:00
if ( ( ph [ 0 ] - > config_start > num_sects ) | |
( ( ph [ 0 ] - > config_start + ph [ 0 ] - > config_size ) > num_sects ) ) {
ldm_crit ( " Database extends beyond the end of the disk. " ) ;
goto out ;
}
if ( ( ph [ 0 ] - > logical_disk_start > ph [ 0 ] - > config_start ) | |
( ( ph [ 0 ] - > logical_disk_start + ph [ 0 ] - > logical_disk_size )
> ph [ 0 ] - > config_start ) ) {
ldm_crit ( " Disk and database overlap. " ) ;
goto out ;
}
if ( ! ldm_compare_privheads ( ph [ 0 ] , ph [ 1 ] ) ) {
ldm_crit ( " Primary and backup PRIVHEADs don't match. " ) ;
goto out ;
}
/* FIXME ignore this for now
if ( ! ldm_compare_privheads ( ph [ 0 ] , ph [ 2 ] ) ) {
ldm_crit ( " Primary and backup PRIVHEADs don't match. " ) ;
goto out ;
} */
ldm_debug ( " Validated PRIVHEADs successfully. " ) ;
2006-10-01 10:27:15 +04:00
result = true ;
2005-04-17 02:20:36 +04:00
out :
kfree ( ph [ 1 ] ) ;
kfree ( ph [ 2 ] ) ;
return result ;
}
/**
* ldm_validate_tocblocks - Validate the table of contents and its backups
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2021-08-10 18:45:09 +03:00
* @ base : Offset , into @ state - > disk , of the database
2005-04-17 02:20:36 +04:00
* @ ldb : Cache of the database structures
*
* Find and compare the four tables of contents of the LDM Database stored on
2021-08-10 18:45:09 +03:00
* @ state - > disk and return the parsed information into @ toc1 .
2005-04-17 02:20:36 +04:00
*
* The offsets and sizes of the configs are range - checked against a privhead .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ toc1 contains validated TOCBLOCK info
* ' false ' @ toc1 contents are undefined
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
static bool ldm_validate_tocblocks ( struct parsed_partitions * state ,
unsigned long base , struct ldmdb * ldb )
2005-04-17 02:20:36 +04:00
{
static const int off [ 4 ] = { OFF_TOCB1 , OFF_TOCB2 , OFF_TOCB3 , OFF_TOCB4 } ;
struct tocblock * tb [ 4 ] ;
struct privhead * ph ;
Sector sect ;
u8 * data ;
2007-05-21 12:37:42 +04:00
int i , nr_tbs ;
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state | | ! ldb ) ;
2007-05-21 12:37:42 +04:00
ph = & ldb - > ph ;
2005-04-17 02:20:36 +04:00
tb [ 0 ] = & ldb - > toc ;
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 23:55:00 +03:00
tb [ 1 ] = kmalloc_array ( 3 , sizeof ( * tb [ 1 ] ) , GFP_KERNEL ) ;
2007-05-21 12:37:42 +04:00
if ( ! tb [ 1 ] ) {
ldm_crit ( " Out of memory. " ) ;
goto err ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
tb [ 2 ] = ( struct tocblock * ) ( ( u8 * ) tb [ 1 ] + sizeof ( * tb [ 1 ] ) ) ;
tb [ 3 ] = ( struct tocblock * ) ( ( u8 * ) tb [ 2 ] + sizeof ( * tb [ 2 ] ) ) ;
/*
* Try to read and parse all four TOCBLOCKs .
*
* Windows Vista LDM v2 .12 does not always have all four TOCBLOCKs so
* skip any that fail as long as we get at least one valid TOCBLOCK .
*/
for ( nr_tbs = i = 0 ; i < 4 ; i + + ) {
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , base + off [ i ] , & sect ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
2007-05-21 12:37:42 +04:00
ldm_error ( " Disk read failed for TOCBLOCK %d. " , i ) ;
continue ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
if ( ldm_parse_tocblock ( data , tb [ nr_tbs ] ) )
nr_tbs + + ;
put_dev_sector ( sect ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
if ( ! nr_tbs ) {
ldm_crit ( " Failed to find a valid TOCBLOCK. " ) ;
goto err ;
}
/* Range check the TOCBLOCK against a privhead. */
2005-04-17 02:20:36 +04:00
if ( ( ( tb [ 0 ] - > bitmap1_start + tb [ 0 ] - > bitmap1_size ) > ph - > config_size ) | |
2007-05-21 12:37:42 +04:00
( ( tb [ 0 ] - > bitmap2_start + tb [ 0 ] - > bitmap2_size ) >
ph - > config_size ) ) {
ldm_crit ( " The bitmaps are out of range. Giving up. " ) ;
goto err ;
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
/* Compare all loaded TOCBLOCKs. */
for ( i = 1 ; i < nr_tbs ; i + + ) {
if ( ! ldm_compare_tocblocks ( tb [ 0 ] , tb [ i ] ) ) {
ldm_crit ( " TOCBLOCKs 0 and %d do not match. " , i ) ;
goto err ;
}
2005-04-17 02:20:36 +04:00
}
2007-05-21 12:37:42 +04:00
ldm_debug ( " Validated %d TOCBLOCKs successfully. " , nr_tbs ) ;
2006-10-01 10:27:15 +04:00
result = true ;
2007-05-21 12:37:42 +04:00
err :
kfree ( tb [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
return result ;
}
/**
* ldm_validate_vmdb - Read the VMDB and validate it
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2005-04-17 02:20:36 +04:00
* @ base : Offset , into @ bdev , of the database
* @ ldb : Cache of the database structures
*
* Find the vmdb of the LDM Database stored on @ bdev and return the parsed
* information in @ ldb .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ ldb contains validated VBDB info
* ' false ' @ ldb contents are undefined
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
static bool ldm_validate_vmdb ( struct parsed_partitions * state ,
unsigned long base , struct ldmdb * ldb )
2005-04-17 02:20:36 +04:00
{
Sector sect ;
u8 * data ;
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
struct vmdb * vm ;
struct tocblock * toc ;
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state | | ! ldb ) ;
2005-04-17 02:20:36 +04:00
vm = & ldb - > vm ;
toc = & ldb - > toc ;
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , base + OFF_VMDB , & sect ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
ldm_crit ( " Disk read failed. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
if ( ! ldm_parse_vmdb ( data , vm ) )
goto out ; /* Already logged */
/* Are there uncommitted transactions? */
2008-07-25 12:45:25 +04:00
if ( get_unaligned_be16 ( data + 0x10 ) ! = 0x01 ) {
2005-04-17 02:20:36 +04:00
ldm_crit ( " Database is not in a consistent state. Aborting. " ) ;
goto out ;
}
if ( vm - > vblk_offset ! = 512 )
ldm_info ( " VBLKs start at offset 0x%04x. " , vm - > vblk_offset ) ;
/*
* The last_vblkd_seq can be before the end of the vmdb , just make sure
* it is not out of bounds .
*/
if ( ( vm - > vblk_size * vm - > last_vblk_seq ) > ( toc - > bitmap1_size < < 9 ) ) {
ldm_crit ( " VMDB exceeds allowed size specified by TOCBLOCK. "
" Database is corrupt. Aborting. " ) ;
goto out ;
}
2006-10-01 10:27:15 +04:00
result = true ;
2005-04-17 02:20:36 +04:00
out :
put_dev_sector ( sect ) ;
return result ;
}
/**
* ldm_validate_partition_table - Determine whether bdev might be a dynamic disk
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2005-04-17 02:20:36 +04:00
*
* This function provides a weak test to decide whether the device is a dynamic
* disk or not . It looks for an MS - DOS - style partition table containing at
* least one partition of type 0x42 ( formerly SFS , now used by Windows for
* dynamic disks ) .
*
2010-05-15 22:09:30 +04:00
* N . B . The only possible error can come from the read_part_sector and that is
2005-04-17 02:20:36 +04:00
* only likely to happen if the underlying device is strange . If that IS
* the case we should return zero to let someone else try .
*
2021-08-10 18:45:09 +03:00
* Return : ' true ' @ state - > disk is a dynamic disk
* ' false ' @ state - > disk is not a dynamic disk , or an error occurred
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
static bool ldm_validate_partition_table ( struct parsed_partitions * state )
2005-04-17 02:20:36 +04:00
{
Sector sect ;
u8 * data ;
2020-03-24 10:25:26 +03:00
struct msdos_partition * p ;
2005-04-17 02:20:36 +04:00
int i ;
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state ) ;
2005-04-17 02:20:36 +04:00
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , 0 , & sect ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
2011-03-17 08:34:14 +03:00
ldm_info ( " Disk read failed. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
if ( * ( __le16 * ) ( data + 0x01FE ) ! = cpu_to_le16 ( MSDOS_LABEL_MAGIC ) )
goto out ;
2020-03-24 10:25:26 +03:00
p = ( struct msdos_partition * ) ( data + 0x01BE ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < 4 ; i + + , p + + )
2021-05-08 01:07:53 +03:00
if ( p - > sys_ind = = LDM_PARTITION ) {
2006-10-01 10:27:15 +04:00
result = true ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( result )
ldm_debug ( " Found W2K dynamic disk partition type. " ) ;
out :
put_dev_sector ( sect ) ;
return result ;
}
/**
* ldm_get_disk_objid - Search a linked list of vblk ' s for a given Disk Id
* @ ldb : Cache of the database structures
*
* The LDM Database contains a list of all partitions on all dynamic disks .
* The primary PRIVHEAD , at the beginning of the physical disk , tells us
* the GUID of this disk . This function searches for the GUID in a linked
* list of vblk ' s .
*
* Return : Pointer , A matching vblk was found
* NULL , No match , or an error
*/
static struct vblk * ldm_get_disk_objid ( const struct ldmdb * ldb )
{
struct list_head * item ;
BUG_ON ( ! ldb ) ;
list_for_each ( item , & ldb - > v_disk ) {
struct vblk * v = list_entry ( item , struct vblk , list ) ;
2017-05-17 10:38:37 +03:00
if ( uuid_equal ( & v - > vblk . disk . disk_id , & ldb - > ph . disk_id ) )
2005-04-17 02:20:36 +04:00
return v ;
}
return NULL ;
}
/**
* ldm_create_data_partitions - Create data partitions for this device
* @ pp : List of the partitions parsed so far
* @ ldb : Cache of the database structures
*
* The database contains ALL the partitions for ALL disk groups , so we need to
* filter out this specific disk . Using the disk ' s object id , we can find all
* the partitions in the database that belong to this disk .
*
* Add each partition in our database , to the parsed_partitions structure .
*
* N . B . This function creates the partitions in the order it finds partition
* objects in the linked list .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' Partition created
* ' false ' Error , probably a range checking problem
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_create_data_partitions ( struct parsed_partitions * pp ,
2005-04-17 02:20:36 +04:00
const struct ldmdb * ldb )
{
struct list_head * item ;
struct vblk * vb ;
struct vblk * disk ;
struct vblk_part * part ;
int part_num = 1 ;
BUG_ON ( ! pp | | ! ldb ) ;
disk = ldm_get_disk_objid ( ldb ) ;
if ( ! disk ) {
ldm_crit ( " Can't find the ID of this disk in the database. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2010-08-11 05:03:14 +04:00
strlcat ( pp - > pp_buf , " [LDM] " , PAGE_SIZE ) ;
2005-04-17 02:20:36 +04:00
/* Create the data partitions */
list_for_each ( item , & ldb - > v_part ) {
vb = list_entry ( item , struct vblk , list ) ;
part = & vb - > vblk . part ;
if ( part - > disk_id ! = disk - > obj_id )
continue ;
put_partition ( pp , part_num , ldb - > ph . logical_disk_start +
part - > start , part - > size ) ;
part_num + + ;
}
2010-08-11 05:03:14 +04:00
strlcat ( pp - > pp_buf , " \n " , PAGE_SIZE ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_relative - Calculate the next relative offset
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ base : Size of the previous fixed width fields
* @ offset : Cumulative size of the previous variable - width fields
*
* Because many of the VBLK fields are variable - width , it ' s necessary
* to calculate each offset based on the previous one and the length
* of the field it pointed to .
*
* Return : - 1 Error , the calculated offset exceeded the size of the buffer
* n OK , a range - checked offset into buffer
*/
2007-07-16 22:39:02 +04:00
static int ldm_relative ( const u8 * buffer , int buflen , int base , int offset )
2005-04-17 02:20:36 +04:00
{
base + = offset ;
2007-07-16 22:39:02 +04:00
if ( ! buffer | | offset < 0 | | base > buflen ) {
if ( ! buffer )
ldm_error ( " !buffer " ) ;
if ( offset < 0 )
ldm_error ( " offset (%d) < 0 " , offset ) ;
if ( base > buflen )
ldm_error ( " base (%d) > buflen (%d) " , base , buflen ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
2007-07-16 22:39:02 +04:00
}
if ( base + buffer [ base ] > = buflen ) {
ldm_error ( " base (%d) + buffer[base] (%d) >= buflen (%d) " , base ,
buffer [ base ] , buflen ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
2007-07-16 22:39:02 +04:00
}
2005-04-17 02:20:36 +04:00
return buffer [ base ] + offset + 1 ;
}
/**
* ldm_get_vnum - Convert a variable - width , big endian number , into cpu order
* @ block : Pointer to the variable - width number to convert
*
* Large numbers in the LDM Database are often stored in a packed format . Each
* number is prefixed by a one byte width marker . All numbers in the database
* are stored in big - endian byte order . This function reads one of these
* numbers and returns the result
*
* N . B . This function DOES NOT perform any range checking , though the most
* it will read is eight bytes .
*
* Return : n A number
* 0 Zero , or an error occurred
*/
static u64 ldm_get_vnum ( const u8 * block )
{
u64 tmp = 0 ;
u8 length ;
BUG_ON ( ! block ) ;
length = * block + + ;
if ( length & & length < = 8 )
while ( length - - )
tmp = ( tmp < < 8 ) | * block + + ;
else
ldm_error ( " Illegal length %d. " , length ) ;
return tmp ;
}
/**
* ldm_get_vstr - Read a length - prefixed string into a buffer
* @ block : Pointer to the length marker
* @ buffer : Location to copy string to
* @ buflen : Size of the output buffer
*
* Many of the strings in the LDM Database are not NULL terminated . Instead
* they are prefixed by a one byte length marker . This function copies one of
* these strings into a buffer .
*
* N . B . This function DOES NOT perform any range checking on the input .
* If the buffer is too small , the output will be truncated .
*
* Return : 0 , Error and @ buffer contents are undefined
* n , String length in characters ( excluding NULL )
* buflen - 1 , String was truncated .
*/
static int ldm_get_vstr ( const u8 * block , u8 * buffer , int buflen )
{
int length ;
BUG_ON ( ! block | | ! buffer ) ;
length = block [ 0 ] ;
if ( length > = buflen ) {
ldm_error ( " Truncating string %d -> %d. " , length , buflen ) ;
length = buflen - 1 ;
}
memcpy ( buffer , block + 1 , length ) ;
buffer [ length ] = 0 ;
return length ;
}
/**
* ldm_parse_cmp3 - Read a raw VBLK Component object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Component object ( version 3 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Component VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_cmp3 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
int r_objid , r_name , r_vstate , r_child , r_parent , r_stripe , r_cols , len ;
struct vblk_comp * comp ;
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
r_vstate = ldm_relative ( buffer , buflen , 0x18 , r_name ) ;
r_child = ldm_relative ( buffer , buflen , 0x1D , r_vstate ) ;
r_parent = ldm_relative ( buffer , buflen , 0x2D , r_child ) ;
if ( buffer [ 0x12 ] & VBLK_FLAG_COMP_STRIPE ) {
r_stripe = ldm_relative ( buffer , buflen , 0x2E , r_parent ) ;
r_cols = ldm_relative ( buffer , buflen , 0x2E , r_stripe ) ;
len = r_cols ;
} else {
r_stripe = 0 ;
len = r_parent ;
}
if ( len < 0 )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_CMP3 ;
2008-07-25 12:45:25 +04:00
if ( len ! = get_unaligned_be32 ( buffer + 0x14 ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
comp = & vb - > vblk . comp ;
ldm_get_vstr ( buffer + 0x18 + r_name , comp - > state ,
sizeof ( comp - > state ) ) ;
comp - > type = buffer [ 0x18 + r_vstate ] ;
comp - > children = ldm_get_vnum ( buffer + 0x1D + r_vstate ) ;
comp - > parent_id = ldm_get_vnum ( buffer + 0x2D + r_child ) ;
comp - > chunksize = r_stripe ? ldm_get_vnum ( buffer + r_parent + 0x2E ) : 0 ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_dgr3 - Read a raw VBLK Disk Group object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Disk Group object ( version 3 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Disk Group VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
static int ldm_parse_dgr3 ( const u8 * buffer , int buflen , struct vblk * vb )
{
int r_objid , r_name , r_diskid , r_id1 , r_id2 , len ;
struct vblk_dgrp * dgrp ;
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
r_diskid = ldm_relative ( buffer , buflen , 0x18 , r_name ) ;
if ( buffer [ 0x12 ] & VBLK_FLAG_DGR3_IDS ) {
r_id1 = ldm_relative ( buffer , buflen , 0x24 , r_diskid ) ;
r_id2 = ldm_relative ( buffer , buflen , 0x24 , r_id1 ) ;
len = r_id2 ;
2022-04-23 14:38:11 +03:00
} else
2005-04-17 02:20:36 +04:00
len = r_diskid ;
if ( len < 0 )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_DGR3 ;
2008-07-25 12:45:25 +04:00
if ( len ! = get_unaligned_be32 ( buffer + 0x14 ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
dgrp = & vb - > vblk . dgrp ;
ldm_get_vstr ( buffer + 0x18 + r_name , dgrp - > disk_id ,
sizeof ( dgrp - > disk_id ) ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_dgr4 - Read a raw VBLK Disk Group object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Disk Group object ( version 4 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Disk Group VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_dgr4 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
char buf [ 64 ] ;
int r_objid , r_name , r_id1 , r_id2 , len ;
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
if ( buffer [ 0x12 ] & VBLK_FLAG_DGR4_IDS ) {
r_id1 = ldm_relative ( buffer , buflen , 0x44 , r_name ) ;
r_id2 = ldm_relative ( buffer , buflen , 0x44 , r_id1 ) ;
len = r_id2 ;
2022-04-23 14:38:11 +03:00
} else
2005-04-17 02:20:36 +04:00
len = r_name ;
if ( len < 0 )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_DGR4 ;
2008-07-25 12:45:25 +04:00
if ( len ! = get_unaligned_be32 ( buffer + 0x14 ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
ldm_get_vstr ( buffer + 0x18 + r_objid , buf , sizeof ( buf ) ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_dsk3 - Read a raw VBLK Disk object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Disk object ( version 3 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Disk VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_dsk3 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
int r_objid , r_name , r_diskid , r_altname , len ;
struct vblk_disk * disk ;
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
r_diskid = ldm_relative ( buffer , buflen , 0x18 , r_name ) ;
r_altname = ldm_relative ( buffer , buflen , 0x18 , r_diskid ) ;
len = r_altname ;
if ( len < 0 )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_DSK3 ;
2008-07-25 12:45:25 +04:00
if ( len ! = get_unaligned_be32 ( buffer + 0x14 ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
disk = & vb - > vblk . disk ;
ldm_get_vstr ( buffer + 0x18 + r_diskid , disk - > alt_name ,
sizeof ( disk - > alt_name ) ) ;
2017-05-17 10:38:37 +03:00
if ( uuid_parse ( buffer + 0x19 + r_name , & disk - > disk_id ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_dsk4 - Read a raw VBLK Disk object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Disk object ( version 4 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Disk VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_dsk4 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
int r_objid , r_name , len ;
struct vblk_disk * disk ;
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
len = r_name ;
if ( len < 0 )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_DSK4 ;
2008-07-25 12:45:25 +04:00
if ( len ! = get_unaligned_be32 ( buffer + 0x14 ) )
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
disk = & vb - > vblk . disk ;
2020-04-22 16:03:17 +03:00
import_uuid ( & disk - > disk_id , buffer + 0x18 + r_name ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_prt3 - Read a raw VBLK Partition object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Partition object ( version 3 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Partition VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2007-05-21 12:37:42 +04:00
static bool ldm_parse_prt3 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
int r_objid , r_name , r_size , r_parent , r_diskid , r_index , len ;
struct vblk_part * part ;
2007-05-21 12:37:42 +04:00
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
if ( r_objid < 0 ) {
ldm_error ( " r_objid %d < 0 " , r_objid ) ;
return false ;
}
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
if ( r_name < 0 ) {
ldm_error ( " r_name %d < 0 " , r_name ) ;
return false ;
}
r_size = ldm_relative ( buffer , buflen , 0x34 , r_name ) ;
if ( r_size < 0 ) {
ldm_error ( " r_size %d < 0 " , r_size ) ;
return false ;
}
r_parent = ldm_relative ( buffer , buflen , 0x34 , r_size ) ;
if ( r_parent < 0 ) {
ldm_error ( " r_parent %d < 0 " , r_parent ) ;
return false ;
}
r_diskid = ldm_relative ( buffer , buflen , 0x34 , r_parent ) ;
if ( r_diskid < 0 ) {
ldm_error ( " r_diskid %d < 0 " , r_diskid ) ;
return false ;
}
2005-04-17 02:20:36 +04:00
if ( buffer [ 0x12 ] & VBLK_FLAG_PART_INDEX ) {
2007-05-21 12:37:42 +04:00
r_index = ldm_relative ( buffer , buflen , 0x34 , r_diskid ) ;
if ( r_index < 0 ) {
ldm_error ( " r_index %d < 0 " , r_index ) ;
return false ;
}
2005-04-17 02:20:36 +04:00
len = r_index ;
2022-04-23 14:38:11 +03:00
} else
2005-04-17 02:20:36 +04:00
len = r_diskid ;
2007-05-21 12:37:42 +04:00
if ( len < 0 ) {
ldm_error ( " len %d < 0 " , len ) ;
2006-10-01 10:27:15 +04:00
return false ;
2007-05-21 12:37:42 +04:00
}
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_PRT3 ;
2008-07-25 12:45:25 +04:00
if ( len > get_unaligned_be32 ( buffer + 0x14 ) ) {
2007-05-21 12:37:42 +04:00
ldm_error ( " len %d > BE32(buffer + 0x14) %d " , len ,
2008-07-25 12:45:25 +04:00
get_unaligned_be32 ( buffer + 0x14 ) ) ;
2006-10-01 10:27:15 +04:00
return false ;
2007-05-21 12:37:42 +04:00
}
2005-04-17 02:20:36 +04:00
part = & vb - > vblk . part ;
2008-07-25 12:45:25 +04:00
part - > start = get_unaligned_be64 ( buffer + 0x24 + r_name ) ;
part - > volume_offset = get_unaligned_be64 ( buffer + 0x2C + r_name ) ;
2007-05-21 12:37:42 +04:00
part - > size = ldm_get_vnum ( buffer + 0x34 + r_name ) ;
part - > parent_id = ldm_get_vnum ( buffer + 0x34 + r_size ) ;
part - > disk_id = ldm_get_vnum ( buffer + 0x34 + r_parent ) ;
2005-04-17 02:20:36 +04:00
if ( vb - > flags & VBLK_FLAG_PART_INDEX )
part - > partnum = buffer [ 0x35 + r_diskid ] ;
else
part - > partnum = 0 ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_vol5 - Read a raw VBLK Volume object into a vblk structure
* @ buffer : Block of data being worked on
* @ buflen : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK Volume object ( version 5 ) into a vblk structure .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a Volume VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2007-07-16 22:39:02 +04:00
static bool ldm_parse_vol5 ( const u8 * buffer , int buflen , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
2007-07-16 22:39:02 +04:00
int r_objid , r_name , r_vtype , r_disable_drive_letter , r_child , r_size ;
int r_id1 , r_id2 , r_size2 , r_drive , len ;
2005-04-17 02:20:36 +04:00
struct vblk_volu * volu ;
2007-07-16 22:39:02 +04:00
BUG_ON ( ! buffer | | ! vb ) ;
r_objid = ldm_relative ( buffer , buflen , 0x18 , 0 ) ;
if ( r_objid < 0 ) {
ldm_error ( " r_objid %d < 0 " , r_objid ) ;
return false ;
}
r_name = ldm_relative ( buffer , buflen , 0x18 , r_objid ) ;
if ( r_name < 0 ) {
ldm_error ( " r_name %d < 0 " , r_name ) ;
return false ;
}
r_vtype = ldm_relative ( buffer , buflen , 0x18 , r_name ) ;
if ( r_vtype < 0 ) {
ldm_error ( " r_vtype %d < 0 " , r_vtype ) ;
return false ;
}
r_disable_drive_letter = ldm_relative ( buffer , buflen , 0x18 , r_vtype ) ;
if ( r_disable_drive_letter < 0 ) {
ldm_error ( " r_disable_drive_letter %d < 0 " ,
r_disable_drive_letter ) ;
return false ;
}
r_child = ldm_relative ( buffer , buflen , 0x2D , r_disable_drive_letter ) ;
if ( r_child < 0 ) {
ldm_error ( " r_child %d < 0 " , r_child ) ;
return false ;
}
r_size = ldm_relative ( buffer , buflen , 0x3D , r_child ) ;
if ( r_size < 0 ) {
ldm_error ( " r_size %d < 0 " , r_size ) ;
return false ;
}
if ( buffer [ 0x12 ] & VBLK_FLAG_VOLU_ID1 ) {
r_id1 = ldm_relative ( buffer , buflen , 0x52 , r_size ) ;
if ( r_id1 < 0 ) {
ldm_error ( " r_id1 %d < 0 " , r_id1 ) ;
return false ;
}
} else
2005-04-17 02:20:36 +04:00
r_id1 = r_size ;
2007-07-16 22:39:02 +04:00
if ( buffer [ 0x12 ] & VBLK_FLAG_VOLU_ID2 ) {
r_id2 = ldm_relative ( buffer , buflen , 0x52 , r_id1 ) ;
if ( r_id2 < 0 ) {
ldm_error ( " r_id2 %d < 0 " , r_id2 ) ;
return false ;
}
} else
2005-04-17 02:20:36 +04:00
r_id2 = r_id1 ;
2007-07-16 22:39:02 +04:00
if ( buffer [ 0x12 ] & VBLK_FLAG_VOLU_SIZE ) {
r_size2 = ldm_relative ( buffer , buflen , 0x52 , r_id2 ) ;
if ( r_size2 < 0 ) {
ldm_error ( " r_size2 %d < 0 " , r_size2 ) ;
return false ;
}
} else
2005-04-17 02:20:36 +04:00
r_size2 = r_id2 ;
2007-07-16 22:39:02 +04:00
if ( buffer [ 0x12 ] & VBLK_FLAG_VOLU_DRIVE ) {
r_drive = ldm_relative ( buffer , buflen , 0x52 , r_size2 ) ;
if ( r_drive < 0 ) {
ldm_error ( " r_drive %d < 0 " , r_drive ) ;
return false ;
}
} else
2005-04-17 02:20:36 +04:00
r_drive = r_size2 ;
len = r_drive ;
2007-07-16 22:39:02 +04:00
if ( len < 0 ) {
ldm_error ( " len %d < 0 " , len ) ;
2006-10-01 10:27:15 +04:00
return false ;
2007-07-16 22:39:02 +04:00
}
2005-04-17 02:20:36 +04:00
len + = VBLK_SIZE_VOL5 ;
2008-07-25 12:45:25 +04:00
if ( len > get_unaligned_be32 ( buffer + 0x14 ) ) {
2007-07-16 22:39:02 +04:00
ldm_error ( " len %d > BE32(buffer + 0x14) %d " , len ,
2008-07-25 12:45:25 +04:00
get_unaligned_be32 ( buffer + 0x14 ) ) ;
2006-10-01 10:27:15 +04:00
return false ;
2007-07-16 22:39:02 +04:00
}
2005-04-17 02:20:36 +04:00
volu = & vb - > vblk . volu ;
2007-07-16 22:39:02 +04:00
ldm_get_vstr ( buffer + 0x18 + r_name , volu - > volume_type ,
sizeof ( volu - > volume_type ) ) ;
memcpy ( volu - > volume_state , buffer + 0x18 + r_disable_drive_letter ,
sizeof ( volu - > volume_state ) ) ;
volu - > size = ldm_get_vnum ( buffer + 0x3D + r_child ) ;
volu - > partition_type = buffer [ 0x41 + r_size ] ;
memcpy ( volu - > guid , buffer + 0x42 + r_size , sizeof ( volu - > guid ) ) ;
2005-04-17 02:20:36 +04:00
if ( buffer [ 0x12 ] & VBLK_FLAG_VOLU_DRIVE ) {
2007-07-16 22:39:02 +04:00
ldm_get_vstr ( buffer + 0x52 + r_size , volu - > drive_hint ,
sizeof ( volu - > drive_hint ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_parse_vblk - Read a raw VBLK object into a vblk structure
* @ buf : Block of data being worked on
* @ len : Size of the block of data
* @ vb : In - memory vblk in which to return information
*
* Read a raw VBLK object into a vblk structure . This function just reads the
* information common to all VBLK types , then delegates the rest of the work to
* helper functions : ldm_parse_ * .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' @ vb contains a VBLK
* ' false ' @ vb contents are not defined
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_parse_vblk ( const u8 * buf , int len , struct vblk * vb )
2005-04-17 02:20:36 +04:00
{
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
int r_objid ;
BUG_ON ( ! buf | | ! vb ) ;
r_objid = ldm_relative ( buf , len , 0x18 , 0 ) ;
if ( r_objid < 0 ) {
ldm_error ( " VBLK header is corrupt. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
vb - > flags = buf [ 0x12 ] ;
vb - > type = buf [ 0x13 ] ;
vb - > obj_id = ldm_get_vnum ( buf + 0x18 ) ;
ldm_get_vstr ( buf + 0x18 + r_objid , vb - > name , sizeof ( vb - > name ) ) ;
switch ( vb - > type ) {
case VBLK_CMP3 : result = ldm_parse_cmp3 ( buf , len , vb ) ; break ;
case VBLK_DSK3 : result = ldm_parse_dsk3 ( buf , len , vb ) ; break ;
case VBLK_DSK4 : result = ldm_parse_dsk4 ( buf , len , vb ) ; break ;
case VBLK_DGR3 : result = ldm_parse_dgr3 ( buf , len , vb ) ; break ;
case VBLK_DGR4 : result = ldm_parse_dgr4 ( buf , len , vb ) ; break ;
case VBLK_PRT3 : result = ldm_parse_prt3 ( buf , len , vb ) ; break ;
case VBLK_VOL5 : result = ldm_parse_vol5 ( buf , len , vb ) ; break ;
}
if ( result )
ldm_debug ( " Parsed VBLK 0x%llx (type: 0x%02x) ok. " ,
( unsigned long long ) vb - > obj_id , vb - > type ) ;
else
ldm_error ( " Failed to parse VBLK 0x%llx (type: 0x%02x). " ,
( unsigned long long ) vb - > obj_id , vb - > type ) ;
return result ;
}
/**
* ldm_ldmdb_add - Adds a raw VBLK entry to the ldmdb database
* @ data : Raw VBLK to add to the database
* @ len : Size of the raw VBLK
* @ ldb : Cache of the database structures
*
* The VBLKs are sorted into categories . Partitions are also sorted by offset .
*
* N . B . This function does not check the validity of the VBLKs .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' The VBLK was added
* ' false ' An error occurred
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_ldmdb_add ( u8 * data , int len , struct ldmdb * ldb )
2005-04-17 02:20:36 +04:00
{
struct vblk * vb ;
struct list_head * item ;
BUG_ON ( ! data | | ! ldb ) ;
vb = kmalloc ( sizeof ( * vb ) , GFP_KERNEL ) ;
if ( ! vb ) {
ldm_crit ( " Out of memory. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
if ( ! ldm_parse_vblk ( data , len , vb ) ) {
kfree ( vb ) ;
2006-10-01 10:27:15 +04:00
return false ; /* Already logged */
2005-04-17 02:20:36 +04:00
}
/* Put vblk into the correct list. */
switch ( vb - > type ) {
case VBLK_DGR3 :
case VBLK_DGR4 :
list_add ( & vb - > list , & ldb - > v_dgrp ) ;
break ;
case VBLK_DSK3 :
case VBLK_DSK4 :
list_add ( & vb - > list , & ldb - > v_disk ) ;
break ;
case VBLK_VOL5 :
list_add ( & vb - > list , & ldb - > v_volu ) ;
break ;
case VBLK_CMP3 :
list_add ( & vb - > list , & ldb - > v_comp ) ;
break ;
case VBLK_PRT3 :
/* Sort by the partition's start sector. */
list_for_each ( item , & ldb - > v_part ) {
struct vblk * v = list_entry ( item , struct vblk , list ) ;
if ( ( v - > vblk . part . disk_id = = vb - > vblk . part . disk_id ) & &
( v - > vblk . part . start > vb - > vblk . part . start ) ) {
list_add_tail ( & vb - > list , & v - > list ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
}
list_add_tail ( & vb - > list , & ldb - > v_part ) ;
break ;
}
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_frag_add - Add a VBLK fragment to a list
* @ data : Raw fragment to be added to the list
* @ size : Size of the raw fragment
* @ frags : Linked list of VBLK fragments
*
* Fragmented VBLKs may not be consecutive in the database , so they are placed
* in a list so they can be pieced together later .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' Success , the VBLK was added to the list
* ' false ' Error , a problem occurred
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_frag_add ( const u8 * data , int size , struct list_head * frags )
2005-04-17 02:20:36 +04:00
{
struct frag * f ;
struct list_head * item ;
int rec , num , group ;
BUG_ON ( ! data | | ! frags ) ;
2011-04-15 02:21:56 +04:00
if ( size < 2 * VBLK_SIZE_HEAD ) {
2020-01-23 03:29:21 +03:00
ldm_error ( " Value of size is too small. " ) ;
2011-04-15 02:21:56 +04:00
return false ;
}
2008-07-25 12:45:25 +04:00
group = get_unaligned_be32 ( data + 0x08 ) ;
rec = get_unaligned_be16 ( data + 0x0C ) ;
num = get_unaligned_be16 ( data + 0x0E ) ;
2005-04-17 02:20:36 +04:00
if ( ( num < 1 ) | | ( num > 4 ) ) {
ldm_error ( " A VBLK claims to have %d parts. " , num ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
2011-04-15 02:21:56 +04:00
if ( rec > = num ) {
ldm_error ( " REC value (%d) exceeds NUM value (%d) " , rec , num ) ;
return false ;
}
2005-04-17 02:20:36 +04:00
list_for_each ( item , frags ) {
f = list_entry ( item , struct frag , list ) ;
if ( f - > group = = group )
goto found ;
}
f = kmalloc ( sizeof ( * f ) + size * num , GFP_KERNEL ) ;
if ( ! f ) {
ldm_crit ( " Out of memory. " ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
f - > group = group ;
f - > num = num ;
f - > rec = rec ;
f - > map = 0xFF < < num ;
list_add_tail ( & f - > list , frags ) ;
found :
2011-05-19 11:24:17 +04:00
if ( rec > = f - > num ) {
ldm_error ( " REC value (%d) exceeds NUM value (%d) " , rec , f - > num ) ;
return false ;
}
2005-04-17 02:20:36 +04:00
if ( f - > map & ( 1 < < rec ) ) {
ldm_error ( " Duplicate VBLK, part %d. " , rec ) ;
f - > map & = 0x7F ; /* Mark the group as broken */
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
f - > map | = ( 1 < < rec ) ;
2012-02-24 13:37:42 +04:00
if ( ! rec )
memcpy ( f - > data , data , VBLK_SIZE_HEAD ) ;
2011-04-15 02:21:56 +04:00
data + = VBLK_SIZE_HEAD ;
size - = VBLK_SIZE_HEAD ;
2012-02-24 13:37:42 +04:00
memcpy ( f - > data + VBLK_SIZE_HEAD + rec * size , data , size ) ;
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_frag_free - Free a linked list of VBLK fragments
* @ list : Linked list of fragments
*
* Free a linked list of VBLK fragments
*
* Return : none
*/
static void ldm_frag_free ( struct list_head * list )
{
struct list_head * item , * tmp ;
BUG_ON ( ! list ) ;
list_for_each_safe ( item , tmp , list )
kfree ( list_entry ( item , struct frag , list ) ) ;
}
/**
* ldm_frag_commit - Validate fragmented VBLKs and add them to the database
* @ frags : Linked list of VBLK fragments
* @ ldb : Cache of the database structures
*
* Now that all the fragmented VBLKs have been collected , they must be added to
* the database for later use .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' All the fragments we added successfully
* ' false ' One or more of the fragments we invalid
2005-04-17 02:20:36 +04:00
*/
2006-10-01 10:27:15 +04:00
static bool ldm_frag_commit ( struct list_head * frags , struct ldmdb * ldb )
2005-04-17 02:20:36 +04:00
{
struct frag * f ;
struct list_head * item ;
BUG_ON ( ! frags | | ! ldb ) ;
list_for_each ( item , frags ) {
f = list_entry ( item , struct frag , list ) ;
if ( f - > map ! = 0xFF ) {
ldm_error ( " VBLK group %d is incomplete (0x%02x). " ,
f - > group , f - > map ) ;
2006-10-01 10:27:15 +04:00
return false ;
2005-04-17 02:20:36 +04:00
}
if ( ! ldm_ldmdb_add ( f - > data , f - > num * ldb - > vm . vblk_size , ldb ) )
2006-10-01 10:27:15 +04:00
return false ; /* Already logged */
2005-04-17 02:20:36 +04:00
}
2006-10-01 10:27:15 +04:00
return true ;
2005-04-17 02:20:36 +04:00
}
/**
* ldm_get_vblks - Read the on - disk database of VBLKs into memory
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2021-08-10 18:45:09 +03:00
* @ base : Offset , into @ state - > disk , of the database
2005-04-17 02:20:36 +04:00
* @ ldb : Cache of the database structures
*
* To use the information from the VBLKs , they need to be read from the disk ,
* unpacked and validated . We cache them in @ ldb according to their type .
*
2006-10-01 10:27:15 +04:00
* Return : ' true ' All the VBLKs were read successfully
* ' false ' An error occurred
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
static bool ldm_get_vblks ( struct parsed_partitions * state , unsigned long base ,
struct ldmdb * ldb )
2005-04-17 02:20:36 +04:00
{
int size , perbuf , skip , finish , s , v , recs ;
u8 * data = NULL ;
Sector sect ;
2006-10-01 10:27:15 +04:00
bool result = false ;
2005-04-17 02:20:36 +04:00
LIST_HEAD ( frags ) ;
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state | | ! ldb ) ;
2005-04-17 02:20:36 +04:00
size = ldb - > vm . vblk_size ;
perbuf = 512 / size ;
skip = ldb - > vm . vblk_offset > > 9 ; /* Bytes to sectors */
finish = ( size * ldb - > vm . last_vblk_seq ) > > 9 ;
for ( s = skip ; s < finish ; s + + ) { /* For each sector */
2010-05-15 22:09:30 +04:00
data = read_part_sector ( state , base + OFF_VMDB + s , & sect ) ;
2005-04-17 02:20:36 +04:00
if ( ! data ) {
ldm_crit ( " Disk read failed. " ) ;
goto out ;
}
for ( v = 0 ; v < perbuf ; v + + , data + = size ) { /* For each vblk */
2008-07-25 12:45:25 +04:00
if ( MAGIC_VBLK ! = get_unaligned_be32 ( data ) ) {
2005-04-17 02:20:36 +04:00
ldm_error ( " Expected to find a VBLK. " ) ;
goto out ;
}
2008-07-25 12:45:25 +04:00
recs = get_unaligned_be16 ( data + 0x0E ) ; /* Number of records */
2005-04-17 02:20:36 +04:00
if ( recs = = 1 ) {
if ( ! ldm_ldmdb_add ( data , size , ldb ) )
goto out ; /* Already logged */
} else if ( recs > 1 ) {
if ( ! ldm_frag_add ( data , size , & frags ) )
goto out ; /* Already logged */
}
/* else Record is not in use, ignore it. */
}
put_dev_sector ( sect ) ;
data = NULL ;
}
result = ldm_frag_commit ( & frags , ldb ) ; /* Failures, already logged */
out :
if ( data )
put_dev_sector ( sect ) ;
ldm_frag_free ( & frags ) ;
return result ;
}
/**
* ldm_free_vblks - Free a linked list of vblk ' s
* @ lh : Head of a linked list of struct vblk
*
* Free a list of vblk ' s and free the memory used to maintain the list .
*
* Return : none
*/
static void ldm_free_vblks ( struct list_head * lh )
{
struct list_head * item , * tmp ;
BUG_ON ( ! lh ) ;
list_for_each_safe ( item , tmp , lh )
kfree ( list_entry ( item , struct vblk , list ) ) ;
}
/**
* ldm_partition - Find out whether a device is a dynamic disk and handle it
2010-05-15 22:09:30 +04:00
* @ state : Partition check state including device holding the LDM Database
2005-04-17 02:20:36 +04:00
*
* This determines whether the device @ bdev is a dynamic disk and if so creates
* the partitions necessary in the gendisk structure pointed to by @ hd .
*
* We create a dummy device 1 , which contains the LDM database , and then create
* each partition described by the LDM database in sequence as devices 2 + . For
* example , if the device is hda , we would have : hda1 : LDM database , hda2 , hda3 ,
* and so on : the actual data containing partitions .
*
2021-08-10 18:45:09 +03:00
* Return : 1 Success , @ state - > disk is a dynamic disk and we handled it
* 0 Success , @ state - > disk is not a dynamic disk
2005-04-17 02:20:36 +04:00
* - 1 An error occurred before enough information had been read
2021-08-10 18:45:09 +03:00
* Or @ state - > disk is a dynamic disk , but it may be corrupted
2005-04-17 02:20:36 +04:00
*/
2010-05-15 22:09:30 +04:00
int ldm_partition ( struct parsed_partitions * state )
2005-04-17 02:20:36 +04:00
{
struct ldmdb * ldb ;
unsigned long base ;
int result = - 1 ;
2010-05-15 22:09:30 +04:00
BUG_ON ( ! state ) ;
2005-04-17 02:20:36 +04:00
/* Look for signs of a Dynamic Disk */
2010-05-15 22:09:30 +04:00
if ( ! ldm_validate_partition_table ( state ) )
2005-04-17 02:20:36 +04:00
return 0 ;
ldb = kmalloc ( sizeof ( * ldb ) , GFP_KERNEL ) ;
if ( ! ldb ) {
ldm_crit ( " Out of memory. " ) ;
goto out ;
}
/* Parse and check privheads. */
2010-05-15 22:09:30 +04:00
if ( ! ldm_validate_privheads ( state , & ldb - > ph ) )
2005-04-17 02:20:36 +04:00
goto out ; /* Already logged */
/* All further references are relative to base (database start). */
base = ldb - > ph . config_start ;
/* Parse and check tocs and vmdb. */
2010-05-15 22:09:30 +04:00
if ( ! ldm_validate_tocblocks ( state , base , ldb ) | |
! ldm_validate_vmdb ( state , base , ldb ) )
2005-04-17 02:20:36 +04:00
goto out ; /* Already logged */
/* Initialize vblk lists in ldmdb struct */
INIT_LIST_HEAD ( & ldb - > v_dgrp ) ;
INIT_LIST_HEAD ( & ldb - > v_disk ) ;
INIT_LIST_HEAD ( & ldb - > v_volu ) ;
INIT_LIST_HEAD ( & ldb - > v_comp ) ;
INIT_LIST_HEAD ( & ldb - > v_part ) ;
2010-05-15 22:09:30 +04:00
if ( ! ldm_get_vblks ( state , base , ldb ) ) {
2005-04-17 02:20:36 +04:00
ldm_crit ( " Failed to read the VBLKs from the database. " ) ;
goto cleanup ;
}
/* Finally, create the data partition devices. */
2010-05-15 22:09:30 +04:00
if ( ldm_create_data_partitions ( state , ldb ) ) {
2005-04-17 02:20:36 +04:00
ldm_debug ( " Parsed LDM database successfully. " ) ;
result = 1 ;
}
/* else Already logged */
cleanup :
ldm_free_vblks ( & ldb - > v_dgrp ) ;
ldm_free_vblks ( & ldb - > v_disk ) ;
ldm_free_vblks ( & ldb - > v_volu ) ;
ldm_free_vblks ( & ldb - > v_comp ) ;
ldm_free_vblks ( & ldb - > v_part ) ;
out :
kfree ( ldb ) ;
return result ;
}