Initial commit from Ventoy@7cbdc5c
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Huffman alphabets
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wimboot.h"
|
||||
#include "huffman.h"
|
||||
|
||||
/**
|
||||
* Transcribe binary value (for debugging)
|
||||
*
|
||||
* @v value Value
|
||||
* @v bits Length of value (in bits)
|
||||
* @ret string Transcribed value
|
||||
*/
|
||||
const char * huffman_bin ( unsigned long value, unsigned int bits ) {
|
||||
static char buf[ ( 8 * sizeof ( value ) ) + 1 /* NUL */ ];
|
||||
char *out = buf;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( bits < sizeof ( buf ) );
|
||||
|
||||
/* Transcribe value */
|
||||
while ( bits-- )
|
||||
*(out++) = ( ( value & ( 1 << bits ) ) ? '1' : '0' );
|
||||
*out = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump Huffman alphabet (for debugging)
|
||||
*
|
||||
* @v alphabet Huffman alphabet
|
||||
*/
|
||||
static void __attribute__ (( unused ))
|
||||
huffman_dump_alphabet ( struct huffman_alphabet *alphabet ) {
|
||||
struct huffman_symbols *sym;
|
||||
unsigned int bits;
|
||||
unsigned int huf;
|
||||
unsigned int i;
|
||||
|
||||
(void)huf;
|
||||
|
||||
/* Dump symbol table for each utilised length */
|
||||
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
|
||||
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
|
||||
sym = &alphabet->huf[ bits - 1 ];
|
||||
if ( sym->freq == 0 )
|
||||
continue;
|
||||
huf = ( sym->start >> sym->shift );
|
||||
DBG ( "Huffman length %d start \"%s\" freq %d:", bits,
|
||||
huffman_bin ( huf, sym->bits ), sym->freq );
|
||||
for ( i = 0 ; i < sym->freq ; i++ ) {
|
||||
DBG ( " %03x", sym->raw[ huf + i ] );
|
||||
}
|
||||
DBG ( "\n" );
|
||||
}
|
||||
|
||||
/* Dump quick lookup table */
|
||||
DBG ( "Huffman quick lookup:" );
|
||||
for ( i = 0 ; i < ( sizeof ( alphabet->lookup ) /
|
||||
sizeof ( alphabet->lookup[0] ) ) ; i++ ) {
|
||||
DBG ( " %d", ( alphabet->lookup[i] + 1 ) );
|
||||
}
|
||||
DBG ( "\n" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Huffman alphabet
|
||||
*
|
||||
* @v alphabet Huffman alphabet
|
||||
* @v lengths Symbol length table
|
||||
* @v count Number of symbols
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int huffman_alphabet ( struct huffman_alphabet *alphabet,
|
||||
uint8_t *lengths, unsigned int count ) {
|
||||
struct huffman_symbols *sym;
|
||||
unsigned int huf;
|
||||
unsigned int cum_freq;
|
||||
unsigned int bits;
|
||||
unsigned int raw;
|
||||
unsigned int adjustment;
|
||||
unsigned int prefix;
|
||||
int empty;
|
||||
int complete;
|
||||
|
||||
/* Clear symbol table */
|
||||
memset ( alphabet->huf, 0, sizeof ( alphabet->huf ) );
|
||||
|
||||
/* Count number of symbols with each Huffman-coded length */
|
||||
empty = 1;
|
||||
for ( raw = 0 ; raw < count ; raw++ ) {
|
||||
bits = lengths[raw];
|
||||
if ( bits ) {
|
||||
alphabet->huf[ bits - 1 ].freq++;
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* In the degenerate case of having no symbols (i.e. an unused
|
||||
* alphabet), generate a trivial alphabet with exactly two
|
||||
* single-bit codes. This allows callers to avoid having to
|
||||
* check for this special case.
|
||||
*/
|
||||
if ( empty )
|
||||
alphabet->huf[0].freq = 2;
|
||||
|
||||
/* Populate Huffman-coded symbol table */
|
||||
huf = 0;
|
||||
cum_freq = 0;
|
||||
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
|
||||
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
|
||||
sym = &alphabet->huf[ bits - 1 ];
|
||||
sym->bits = bits;
|
||||
sym->shift = ( HUFFMAN_BITS - bits );
|
||||
sym->start = ( huf << sym->shift );
|
||||
sym->raw = &alphabet->raw[cum_freq];
|
||||
huf += sym->freq;
|
||||
if ( huf > ( 1U << bits ) ) {
|
||||
DBG ( "Huffman alphabet has too many symbols with "
|
||||
"lengths <=%d\n", bits );
|
||||
return -1;
|
||||
}
|
||||
huf <<= 1;
|
||||
cum_freq += sym->freq;
|
||||
}
|
||||
complete = ( huf == ( 1U << bits ) );
|
||||
|
||||
/* Populate raw symbol table */
|
||||
for ( raw = 0 ; raw < count ; raw++ ) {
|
||||
bits = lengths[raw];
|
||||
if ( bits ) {
|
||||
sym = &alphabet->huf[ bits - 1 ];
|
||||
*(sym->raw++) = raw;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust Huffman-coded symbol table raw pointers and populate
|
||||
* quick lookup table.
|
||||
*/
|
||||
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
|
||||
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
|
||||
sym = &alphabet->huf[ bits - 1 ];
|
||||
|
||||
/* Adjust raw pointer */
|
||||
sym->raw -= sym->freq; /* Reset to first symbol */
|
||||
adjustment = ( sym->start >> sym->shift );
|
||||
sym->raw -= adjustment; /* Adjust for quick indexing */
|
||||
|
||||
/* Populate quick lookup table */
|
||||
for ( prefix = ( sym->start >> HUFFMAN_QL_SHIFT ) ;
|
||||
prefix < ( 1 << HUFFMAN_QL_BITS ) ; prefix++ ) {
|
||||
alphabet->lookup[prefix] = ( bits - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that there are no invalid codes */
|
||||
if ( ! complete ) {
|
||||
DBG ( "Huffman alphabet is incomplete\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Huffman symbol set
|
||||
*
|
||||
* @v alphabet Huffman alphabet
|
||||
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
|
||||
* @ret sym Huffman symbol set
|
||||
*/
|
||||
struct huffman_symbols * huffman_sym ( struct huffman_alphabet *alphabet,
|
||||
unsigned int huf ) {
|
||||
struct huffman_symbols *sym;
|
||||
unsigned int lookup_index;
|
||||
|
||||
/* Find symbol set for this length */
|
||||
lookup_index = ( huf >> HUFFMAN_QL_SHIFT );
|
||||
sym = &alphabet->huf[ alphabet->lookup[ lookup_index ] ];
|
||||
while ( huf < sym->start )
|
||||
sym--;
|
||||
return sym;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef _HUFFMAN_H
|
||||
#define _HUFFMAN_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Huffman alphabets
|
||||
*
|
||||
*/
|
||||
|
||||
/** Maximum length of a Huffman symbol (in bits) */
|
||||
#define HUFFMAN_BITS 16
|
||||
|
||||
/** Raw huffman symbol */
|
||||
typedef uint16_t huffman_raw_symbol_t;
|
||||
|
||||
/** Quick lookup length for a Huffman symbol (in bits)
|
||||
*
|
||||
* This is a policy decision.
|
||||
*/
|
||||
#define HUFFMAN_QL_BITS 7
|
||||
|
||||
/** Quick lookup shift */
|
||||
#define HUFFMAN_QL_SHIFT ( HUFFMAN_BITS - HUFFMAN_QL_BITS )
|
||||
|
||||
/** A Huffman-coded set of symbols of a given length */
|
||||
struct huffman_symbols {
|
||||
/** Length of Huffman-coded symbols (in bits) */
|
||||
uint8_t bits;
|
||||
/** Shift to normalise symbols of this length to HUFFMAN_BITS bits */
|
||||
uint8_t shift;
|
||||
/** Number of Huffman-coded symbols having this length */
|
||||
uint16_t freq;
|
||||
/** First symbol of this length (normalised to HUFFMAN_BITS bits)
|
||||
*
|
||||
* Stored as a 32-bit value to allow the value
|
||||
* (1<<HUFFMAN_BITS ) to be used for empty sets of symbols
|
||||
* longer than the maximum utilised length.
|
||||
*/
|
||||
uint32_t start;
|
||||
/** Raw symbols having this length */
|
||||
huffman_raw_symbol_t *raw;
|
||||
};
|
||||
|
||||
/** A Huffman-coded alphabet */
|
||||
struct huffman_alphabet {
|
||||
/** Huffman-coded symbol set for each length */
|
||||
struct huffman_symbols huf[HUFFMAN_BITS];
|
||||
/** Quick lookup table */
|
||||
uint8_t lookup[ 1 << HUFFMAN_QL_BITS ];
|
||||
/** Raw symbols
|
||||
*
|
||||
* Ordered by Huffman-coded symbol length, then by symbol
|
||||
* value. This field has a variable length.
|
||||
*/
|
||||
huffman_raw_symbol_t raw[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Huffman symbol length
|
||||
*
|
||||
* @v sym Huffman symbol set
|
||||
* @ret len Length (in bits)
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
huffman_len ( struct huffman_symbols *sym ) {
|
||||
|
||||
return sym->bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Huffman symbol value
|
||||
*
|
||||
* @v sym Huffman symbol set
|
||||
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
|
||||
* @ret raw Raw symbol value
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) huffman_raw_symbol_t
|
||||
huffman_raw ( struct huffman_symbols *sym, unsigned int huf ) {
|
||||
|
||||
return sym->raw[ huf >> sym->shift ];
|
||||
}
|
||||
|
||||
extern int huffman_alphabet ( struct huffman_alphabet *alphabet,
|
||||
uint8_t *lengths, unsigned int count );
|
||||
extern struct huffman_symbols *
|
||||
huffman_sym ( struct huffman_alphabet *alphabet, unsigned int huf );
|
||||
|
||||
#endif /* _HUFFMAN_H */
|
||||
@@ -0,0 +1,668 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* LZX decompression
|
||||
*
|
||||
* This algorithm is derived jointly from the document "[MS-PATCH]:
|
||||
* LZX DELTA Compression and Decompression", available from
|
||||
*
|
||||
* http://msdn.microsoft.com/en-us/library/cc483133.aspx
|
||||
*
|
||||
* and from the file lzx-decompress.c in the wimlib source code.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wimboot.h"
|
||||
#include "huffman.h"
|
||||
#include "lzx.h"
|
||||
|
||||
/** Base positions, indexed by position slot */
|
||||
static unsigned int lzx_position_base[LZX_POSITION_SLOTS];
|
||||
|
||||
/**
|
||||
* Attempt to accumulate bits from LZX bitstream
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v bits Number of bits to accumulate
|
||||
* @v norm_value Accumulated value (normalised to 16 bits)
|
||||
*
|
||||
* Note that there may not be sufficient accumulated bits in the
|
||||
* bitstream; callers must check that sufficient bits are available
|
||||
* before using the value.
|
||||
*/
|
||||
static int lzx_accumulate ( struct lzx *lzx, unsigned int bits ) {
|
||||
const uint16_t *src16;
|
||||
|
||||
/* Accumulate more bits if required */
|
||||
if ( ( lzx->bits < bits ) &&
|
||||
( lzx->input.offset < lzx->input.len ) ) {
|
||||
src16 = (const uint16_t *)( ( char * ) lzx->input.data + lzx->input.offset );
|
||||
lzx->input.offset += sizeof ( *src16 );
|
||||
lzx->accumulator |= ( *src16 << ( 16 - lzx->bits ) );
|
||||
lzx->bits += 16;
|
||||
}
|
||||
|
||||
return ( lzx->accumulator >> 16 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume accumulated bits from LZX bitstream
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v bits Number of bits to consume
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_consume ( struct lzx *lzx, unsigned int bits ) {
|
||||
|
||||
/* Fail if insufficient bits are available */
|
||||
if ( lzx->bits < bits ) {
|
||||
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
|
||||
lzx->input.offset, lzx->input.len, lzx->output.offset );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Consume bits */
|
||||
lzx->accumulator <<= bits;
|
||||
lzx->bits -= bits;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bits from LZX bitstream
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v bits Number of bits to fetch
|
||||
* @ret value Value, or negative error
|
||||
*/
|
||||
static int lzx_getbits ( struct lzx *lzx, unsigned int bits ) {
|
||||
int norm_value;
|
||||
int rc;
|
||||
|
||||
/* Accumulate more bits if required */
|
||||
norm_value = lzx_accumulate ( lzx, bits );
|
||||
|
||||
/* Consume bits */
|
||||
if ( ( rc = lzx_consume ( lzx, bits ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return ( norm_value >> ( 16 - bits ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Align LZX bitstream for byte access
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v bits Minimum number of padding bits
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_align ( struct lzx *lzx, unsigned int bits ) {
|
||||
int pad;
|
||||
|
||||
/* Get padding bits */
|
||||
pad = lzx_getbits ( lzx, bits );
|
||||
if ( pad < 0 )
|
||||
return pad;
|
||||
|
||||
/* Consume all accumulated bits */
|
||||
lzx_consume ( lzx, lzx->bits );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bytes from LZX bitstream
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v data Data buffer, or NULL
|
||||
* @v len Length of data buffer
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_getbytes ( struct lzx *lzx, void *data, size_t len ) {
|
||||
|
||||
/* Sanity check */
|
||||
if ( ( lzx->input.offset + len ) > lzx->input.len ) {
|
||||
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
|
||||
lzx->input.offset, lzx->input.len, lzx->output.offset );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy data */
|
||||
if ( data )
|
||||
memcpy ( data, ( lzx->input.data + lzx->input.offset ), len );
|
||||
lzx->input.offset += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode LZX Huffman-coded symbol
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v alphabet Huffman alphabet
|
||||
* @ret raw Raw symbol, or negative error
|
||||
*/
|
||||
static int lzx_decode ( struct lzx *lzx, struct huffman_alphabet *alphabet ) {
|
||||
struct huffman_symbols *sym;
|
||||
int huf;
|
||||
int rc;
|
||||
|
||||
/* Accumulate sufficient bits */
|
||||
huf = lzx_accumulate ( lzx, HUFFMAN_BITS );
|
||||
if ( huf < 0 )
|
||||
return huf;
|
||||
|
||||
/* Decode symbol */
|
||||
sym = huffman_sym ( alphabet, huf );
|
||||
|
||||
/* Consume bits */
|
||||
if ( ( rc = lzx_consume ( lzx, huffman_len ( sym ) ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return huffman_raw ( sym, huf );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Huffman alphabet from raw length table
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v count Number of symbols
|
||||
* @v bits Length of each length (in bits)
|
||||
* @v lengths Lengths table to fill in
|
||||
* @v alphabet Huffman alphabet to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_raw_alphabet ( struct lzx *lzx, unsigned int count,
|
||||
unsigned int bits, uint8_t *lengths,
|
||||
struct huffman_alphabet *alphabet ) {
|
||||
unsigned int i;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Read lengths */
|
||||
for ( i = 0 ; i < count ; i++ ) {
|
||||
len = lzx_getbits ( lzx, bits );
|
||||
if ( len < 0 )
|
||||
return len;
|
||||
lengths[i] = len;
|
||||
}
|
||||
|
||||
/* Generate Huffman alphabet */
|
||||
if ( ( rc = huffman_alphabet ( alphabet, lengths, count ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate pretree
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @v count Number of symbols
|
||||
* @v lengths Lengths table to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_pretree ( struct lzx *lzx, unsigned int count,
|
||||
uint8_t *lengths ) {
|
||||
unsigned int i;
|
||||
unsigned int length;
|
||||
int dup = 0;
|
||||
int code;
|
||||
int rc;
|
||||
|
||||
/* Generate pretree alphabet */
|
||||
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_PRETREE_CODES,
|
||||
LZX_PRETREE_BITS, lzx->pretree_lengths,
|
||||
&lzx->pretree ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Read lengths */
|
||||
for ( i = 0 ; i < count ; i++ ) {
|
||||
|
||||
if ( dup ) {
|
||||
|
||||
/* Duplicate previous length */
|
||||
lengths[i] = lengths[ i - 1 ];
|
||||
dup--;
|
||||
|
||||
} else {
|
||||
|
||||
/* Get next code */
|
||||
code = lzx_decode ( lzx, &lzx->pretree );
|
||||
if ( code < 0 )
|
||||
return code;
|
||||
|
||||
/* Interpret code */
|
||||
if ( code <= 16 ) {
|
||||
length = ( ( lengths[i] - code + 17 ) % 17 );
|
||||
} else if ( code == 17 ) {
|
||||
length = 0;
|
||||
dup = lzx_getbits ( lzx, 4 );
|
||||
if ( dup < 0 )
|
||||
return dup;
|
||||
dup += 3;
|
||||
} else if ( code == 18 ) {
|
||||
length = 0;
|
||||
dup = lzx_getbits ( lzx, 5 );
|
||||
if ( dup < 0 )
|
||||
return dup;
|
||||
dup += 19;
|
||||
} else if ( code == 19 ) {
|
||||
length = 0;
|
||||
dup = lzx_getbits ( lzx, 1 );
|
||||
if ( dup < 0 )
|
||||
return dup;
|
||||
dup += 3;
|
||||
code = lzx_decode ( lzx, &lzx->pretree );
|
||||
if ( code < 0 )
|
||||
return code;
|
||||
length = ( ( lengths[i] - code + 17 ) % 17 );
|
||||
} else {
|
||||
DBG ( "Unrecognised pretree code %d\n", code );
|
||||
return -1;
|
||||
}
|
||||
lengths[i] = length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if ( dup ) {
|
||||
DBG ( "Pretree duplicate overrun\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate aligned offset Huffman alphabet
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_alignoffset_alphabet ( struct lzx *lzx ) {
|
||||
int rc;
|
||||
|
||||
/* Generate aligned offset alphabet */
|
||||
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_ALIGNOFFSET_CODES,
|
||||
LZX_ALIGNOFFSET_BITS,
|
||||
lzx->alignoffset_lengths,
|
||||
&lzx->alignoffset ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate main Huffman alphabet
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_main_alphabet ( struct lzx *lzx ) {
|
||||
int rc;
|
||||
|
||||
/* Generate literal symbols pretree */
|
||||
if ( ( rc = lzx_pretree ( lzx, LZX_MAIN_LIT_CODES,
|
||||
lzx->main_lengths.literals ) ) != 0 ) {
|
||||
DBG ( "Could not construct main literal pretree\n" );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Generate remaining symbols pretree */
|
||||
if ( ( rc = lzx_pretree ( lzx, ( LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ),
|
||||
lzx->main_lengths.remainder ) ) != 0 ) {
|
||||
DBG ( "Could not construct main remainder pretree\n" );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Generate Huffman alphabet */
|
||||
if ( ( rc = huffman_alphabet ( &lzx->main, lzx->main_lengths.literals,
|
||||
LZX_MAIN_CODES ) ) != 0 ) {
|
||||
DBG ( "Could not generate main alphabet\n" );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate length Huffman alphabet
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_length_alphabet ( struct lzx *lzx ) {
|
||||
int rc;
|
||||
|
||||
/* Generate pretree */
|
||||
if ( ( rc = lzx_pretree ( lzx, LZX_LENGTH_CODES,
|
||||
lzx->length_lengths ) ) != 0 ) {
|
||||
DBG ( "Could not generate length pretree\n" );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Generate Huffman alphabet */
|
||||
if ( ( rc = huffman_alphabet ( &lzx->length, lzx->length_lengths,
|
||||
LZX_LENGTH_CODES ) ) != 0 ) {
|
||||
DBG ( "Could not generate length alphabet\n" );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process LZX block header
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_block_header ( struct lzx *lzx ) {
|
||||
size_t block_len;
|
||||
int block_type;
|
||||
int default_len;
|
||||
int len_high;
|
||||
int len_low;
|
||||
int rc;
|
||||
|
||||
/* Get block type */
|
||||
block_type = lzx_getbits ( lzx, LZX_BLOCK_TYPE_BITS );
|
||||
if ( block_type < 0 )
|
||||
return block_type;
|
||||
lzx->block_type = block_type;
|
||||
|
||||
/* Check block length */
|
||||
default_len = lzx_getbits ( lzx, 1 );
|
||||
if ( default_len < 0 )
|
||||
return default_len;
|
||||
if ( default_len ) {
|
||||
block_len = LZX_DEFAULT_BLOCK_LEN;
|
||||
} else {
|
||||
len_high = lzx_getbits ( lzx, 8 );
|
||||
if ( len_high < 0 )
|
||||
return len_high;
|
||||
len_low = lzx_getbits ( lzx, 8 );
|
||||
if ( len_low < 0 )
|
||||
return len_low;
|
||||
block_len = ( ( len_high << 8 ) | len_low );
|
||||
}
|
||||
lzx->output.threshold = ( lzx->output.offset + block_len );
|
||||
|
||||
/* Handle block type */
|
||||
switch ( block_type ) {
|
||||
case LZX_BLOCK_ALIGNOFFSET :
|
||||
/* Generated aligned offset alphabet */
|
||||
if ( ( rc = lzx_alignoffset_alphabet ( lzx ) ) != 0 )
|
||||
return rc;
|
||||
/* Fall through */
|
||||
case LZX_BLOCK_VERBATIM :
|
||||
/* Generate main alphabet */
|
||||
if ( ( rc = lzx_main_alphabet ( lzx ) ) != 0 )
|
||||
return rc;
|
||||
/* Generate lengths alphabet */
|
||||
if ( ( rc = lzx_length_alphabet ( lzx ) ) != 0 )
|
||||
return rc;
|
||||
break;
|
||||
case LZX_BLOCK_UNCOMPRESSED :
|
||||
/* Align input stream */
|
||||
if ( ( rc = lzx_align ( lzx, 1 ) ) != 0 )
|
||||
return rc;
|
||||
/* Read new repeated offsets */
|
||||
if ( ( rc = lzx_getbytes ( lzx, &lzx->repeated_offset,
|
||||
sizeof ( lzx->repeated_offset )))!=0)
|
||||
return rc;
|
||||
break;
|
||||
default:
|
||||
DBG ( "Unrecognised block type %d\n", block_type );
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process uncompressed data
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lzx_uncompressed ( struct lzx *lzx ) {
|
||||
void *data;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Copy bytes */
|
||||
data = ( lzx->output.data ?
|
||||
( lzx->output.data + lzx->output.offset ) : NULL );
|
||||
len = ( lzx->output.threshold - lzx->output.offset );
|
||||
if ( ( rc = lzx_getbytes ( lzx, data, len ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Align input stream */
|
||||
if ( len % 2 )
|
||||
lzx->input.offset++;
|
||||
|
||||
lzx->output.offset += len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an LZX token
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* Variable names are chosen to match the LZX specification
|
||||
* pseudo-code.
|
||||
*/
|
||||
static int lzx_token ( struct lzx *lzx ) {
|
||||
unsigned int length_header;
|
||||
unsigned int position_slot;
|
||||
unsigned int offset_bits;
|
||||
unsigned int i;
|
||||
size_t match_offset;
|
||||
size_t match_length;
|
||||
int verbatim_bits;
|
||||
int aligned_bits;
|
||||
int maindata;
|
||||
int length;
|
||||
uint8_t *copy;
|
||||
|
||||
/* Get maindata symelse*/
|
||||
maindata = lzx_decode ( lzx, &lzx->main );
|
||||
if ( maindata < 0 )
|
||||
return maindata;
|
||||
|
||||
/* Check for literals */
|
||||
if ( maindata < LZX_MAIN_LIT_CODES ) {
|
||||
if ( lzx->output.data )
|
||||
lzx->output.data[lzx->output.offset] = maindata;
|
||||
lzx->output.offset++;
|
||||
return 0;
|
||||
}
|
||||
maindata -= LZX_MAIN_LIT_CODES;
|
||||
|
||||
/* Calculate the match length */
|
||||
length_header = ( maindata & 7 );
|
||||
if ( length_header == 7 ) {
|
||||
length = lzx_decode ( lzx, &lzx->length );
|
||||
if ( length < 0 )
|
||||
return length;
|
||||
} else {
|
||||
length = 0;
|
||||
}
|
||||
match_length = ( length_header + 2 + length );
|
||||
|
||||
/* Calculate the position slot */
|
||||
position_slot = ( maindata >> 3 );
|
||||
if ( position_slot < LZX_REPEATED_OFFSETS ) {
|
||||
|
||||
/* Repeated offset */
|
||||
match_offset = lzx->repeated_offset[position_slot];
|
||||
lzx->repeated_offset[position_slot] = lzx->repeated_offset[0];
|
||||
lzx->repeated_offset[0] = match_offset;
|
||||
|
||||
} else {
|
||||
|
||||
/* Non-repeated offset */
|
||||
offset_bits = lzx_footer_bits ( position_slot );
|
||||
if ( ( lzx->block_type == LZX_BLOCK_ALIGNOFFSET ) &&
|
||||
( offset_bits >= 3 ) ) {
|
||||
verbatim_bits = lzx_getbits ( lzx, ( offset_bits - 3 ));
|
||||
if ( verbatim_bits < 0 )
|
||||
return verbatim_bits;
|
||||
verbatim_bits <<= 3;
|
||||
aligned_bits = lzx_decode ( lzx, &lzx->alignoffset );
|
||||
if ( aligned_bits < 0 )
|
||||
return aligned_bits;
|
||||
} else {
|
||||
verbatim_bits = lzx_getbits ( lzx, offset_bits );
|
||||
if ( verbatim_bits < 0 )
|
||||
return verbatim_bits;
|
||||
aligned_bits = 0;
|
||||
}
|
||||
match_offset = ( lzx_position_base[position_slot] +
|
||||
verbatim_bits + aligned_bits - 2 );
|
||||
|
||||
/* Update repeated offset list */
|
||||
for ( i = ( LZX_REPEATED_OFFSETS - 1 ) ; i > 0 ; i-- )
|
||||
lzx->repeated_offset[i] = lzx->repeated_offset[ i - 1 ];
|
||||
lzx->repeated_offset[0] = match_offset;
|
||||
}
|
||||
|
||||
/* Copy data */
|
||||
if ( match_offset > lzx->output.offset ) {
|
||||
DBG ( "LZX match underrun out 0x%x offset 0x%x len 0x%x\n",
|
||||
lzx->output.offset, match_offset, match_length );
|
||||
return -1;
|
||||
}
|
||||
if ( lzx->output.data ) {
|
||||
copy = &lzx->output.data[lzx->output.offset];
|
||||
for ( i = 0 ; i < match_length ; i++ )
|
||||
copy[i] = copy[ i - match_offset ];
|
||||
}
|
||||
lzx->output.offset += match_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate E8 jump addresses
|
||||
*
|
||||
* @v lzx Decompressor
|
||||
*/
|
||||
static void lzx_translate_jumps ( struct lzx *lzx ) {
|
||||
size_t offset;
|
||||
int32_t *target;
|
||||
|
||||
/* Sanity check */
|
||||
if ( lzx->output.offset < 10 )
|
||||
return;
|
||||
|
||||
/* Scan for jump instructions */
|
||||
for ( offset = 0 ; offset < ( lzx->output.offset - 10 ) ; offset++ ) {
|
||||
|
||||
/* Check for jump instruction */
|
||||
if ( lzx->output.data[offset] != 0xe8 )
|
||||
continue;
|
||||
|
||||
/* Translate jump target */
|
||||
target = ( ( int32_t * ) &lzx->output.data[ offset + 1 ] );
|
||||
if ( *target >= 0 ) {
|
||||
if ( *target < LZX_WIM_MAGIC_FILESIZE )
|
||||
*target -= offset;
|
||||
} else {
|
||||
if ( *target >= -( ( int32_t ) offset ) )
|
||||
*target += LZX_WIM_MAGIC_FILESIZE;
|
||||
}
|
||||
offset += sizeof ( *target );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress LZX-compressed data
|
||||
*
|
||||
* @v data Compressed data
|
||||
* @v len Length of compressed data
|
||||
* @v buf Decompression buffer, or NULL
|
||||
* @ret out_len Length of decompressed data, or negative error
|
||||
*/
|
||||
ssize_t lzx_decompress ( const void *data, size_t len, void *buf ) {
|
||||
struct lzx lzx;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( len % 2 ) {
|
||||
DBG ( "LZX cannot handle odd-length input data\n" );
|
||||
//return -1;
|
||||
}
|
||||
|
||||
/* Initialise global state, if required */
|
||||
if ( ! lzx_position_base[ LZX_POSITION_SLOTS - 1 ] ) {
|
||||
for ( i = 1 ; i < LZX_POSITION_SLOTS ; i++ ) {
|
||||
lzx_position_base[i] =
|
||||
( lzx_position_base[i-1] +
|
||||
( 1 << lzx_footer_bits ( i - 1 ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialise decompressor */
|
||||
memset ( &lzx, 0, sizeof ( lzx ) );
|
||||
lzx.input.data = data;
|
||||
lzx.input.len = len;
|
||||
lzx.output.data = buf;
|
||||
for ( i = 0 ; i < LZX_REPEATED_OFFSETS ; i++ )
|
||||
lzx.repeated_offset[i] = 1;
|
||||
|
||||
/* Process blocks */
|
||||
while ( lzx.input.offset < lzx.input.len ) {
|
||||
|
||||
/* Process block header */
|
||||
if ( ( rc = lzx_block_header ( &lzx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Process block contents */
|
||||
if ( lzx.block_type == LZX_BLOCK_UNCOMPRESSED ) {
|
||||
|
||||
/* Copy uncompressed data */
|
||||
if ( ( rc = lzx_uncompressed ( &lzx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
} else {
|
||||
|
||||
/* Process token stream */
|
||||
while ( lzx.output.offset < lzx.output.threshold ) {
|
||||
if ( ( rc = lzx_token ( &lzx ) ) != 0 )
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Postprocess to undo E8 jump compression */
|
||||
if ( lzx.output.data )
|
||||
lzx_translate_jumps ( &lzx );
|
||||
|
||||
return lzx.output.offset;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
#ifndef _LZX_H
|
||||
#define _LZX_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2014 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* LZX decompression
|
||||
*
|
||||
*/
|
||||
|
||||
#include "huffman.h"
|
||||
|
||||
/** Number of aligned offset codes */
|
||||
#define LZX_ALIGNOFFSET_CODES 8
|
||||
|
||||
/** Aligned offset code length (in bits) */
|
||||
#define LZX_ALIGNOFFSET_BITS 3
|
||||
|
||||
/** Number of pretree codes */
|
||||
#define LZX_PRETREE_CODES 20
|
||||
|
||||
/** Pretree code length (in bits) */
|
||||
#define LZX_PRETREE_BITS 4
|
||||
|
||||
/** Number of literal main codes */
|
||||
#define LZX_MAIN_LIT_CODES 256
|
||||
|
||||
/** Number of position slots */
|
||||
#define LZX_POSITION_SLOTS 30
|
||||
|
||||
/** Number of main codes */
|
||||
#define LZX_MAIN_CODES ( LZX_MAIN_LIT_CODES + ( 8 * LZX_POSITION_SLOTS ) )
|
||||
|
||||
/** Number of length codes */
|
||||
#define LZX_LENGTH_CODES 249
|
||||
|
||||
/** Block type length (in bits) */
|
||||
#define LZX_BLOCK_TYPE_BITS 3
|
||||
|
||||
/** Default block length */
|
||||
#define LZX_DEFAULT_BLOCK_LEN 32768
|
||||
|
||||
/** Number of repeated offsets */
|
||||
#define LZX_REPEATED_OFFSETS 3
|
||||
|
||||
/** Don't ask */
|
||||
#define LZX_WIM_MAGIC_FILESIZE 12000000
|
||||
|
||||
/** Block types */
|
||||
enum lzx_block_type {
|
||||
/** Verbatim block */
|
||||
LZX_BLOCK_VERBATIM = 1,
|
||||
/** Aligned offset block */
|
||||
LZX_BLOCK_ALIGNOFFSET = 2,
|
||||
/** Uncompressed block */
|
||||
LZX_BLOCK_UNCOMPRESSED = 3,
|
||||
};
|
||||
|
||||
/** An LZX input stream */
|
||||
struct lzx_input_stream {
|
||||
/** Data */
|
||||
const uint8_t *data;
|
||||
/** Length */
|
||||
size_t len;
|
||||
/** Offset within stream */
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
/** An LZX output stream */
|
||||
struct lzx_output_stream {
|
||||
/** Data, or NULL */
|
||||
uint8_t *data;
|
||||
/** Offset within stream */
|
||||
size_t offset;
|
||||
/** End of current block within stream */
|
||||
size_t threshold;
|
||||
};
|
||||
|
||||
/** LZX decompressor */
|
||||
struct lzx {
|
||||
/** Input stream */
|
||||
struct lzx_input_stream input;
|
||||
/** Output stream */
|
||||
struct lzx_output_stream output;
|
||||
/** Accumulator */
|
||||
uint32_t accumulator;
|
||||
/** Number of bits in accumulator */
|
||||
unsigned int bits;
|
||||
/** Block type */
|
||||
enum lzx_block_type block_type;
|
||||
/** Repeated offsets */
|
||||
unsigned int repeated_offset[LZX_REPEATED_OFFSETS];
|
||||
|
||||
/** Aligned offset Huffman alphabet */
|
||||
struct huffman_alphabet alignoffset;
|
||||
/** Aligned offset raw symbols
|
||||
*
|
||||
* Must immediately follow the aligned offset Huffman
|
||||
* alphabet.
|
||||
*/
|
||||
huffman_raw_symbol_t alignoffset_raw[LZX_ALIGNOFFSET_CODES];
|
||||
/** Aligned offset code lengths */
|
||||
uint8_t alignoffset_lengths[LZX_ALIGNOFFSET_CODES];
|
||||
|
||||
/** Pretree Huffman alphabet */
|
||||
struct huffman_alphabet pretree;
|
||||
/** Pretree raw symbols
|
||||
*
|
||||
* Must immediately follow the pretree Huffman alphabet.
|
||||
*/
|
||||
huffman_raw_symbol_t pretree_raw[LZX_PRETREE_CODES];
|
||||
/** Preetree code lengths */
|
||||
uint8_t pretree_lengths[LZX_PRETREE_CODES];
|
||||
|
||||
/** Main Huffman alphabet */
|
||||
struct huffman_alphabet main;
|
||||
/** Main raw symbols
|
||||
*
|
||||
* Must immediately follow the main Huffman alphabet.
|
||||
*/
|
||||
huffman_raw_symbol_t main_raw[LZX_MAIN_CODES];
|
||||
/** Main code lengths */
|
||||
struct {
|
||||
/** Literals */
|
||||
uint8_t literals[LZX_MAIN_LIT_CODES];
|
||||
/** Remaining symbols */
|
||||
uint8_t remainder[ LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ];
|
||||
} __attribute__ (( packed )) main_lengths;
|
||||
|
||||
/** Length Huffman alphabet */
|
||||
struct huffman_alphabet length;
|
||||
/** Length raw symbols
|
||||
*
|
||||
* Must immediately follow the length Huffman alphabet.
|
||||
*/
|
||||
huffman_raw_symbol_t length_raw[LZX_LENGTH_CODES];
|
||||
/** Length code lengths */
|
||||
uint8_t length_lengths[LZX_LENGTH_CODES];
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate number of footer bits for a given position slot
|
||||
*
|
||||
* @v position_slot Position slot
|
||||
* @ret footer_bits Number of footer bits
|
||||
*/
|
||||
static inline unsigned int lzx_footer_bits ( unsigned int position_slot ) {
|
||||
|
||||
if ( position_slot < 2 ) {
|
||||
return 0;
|
||||
} else if ( position_slot < 38 ) {
|
||||
return ( ( position_slot / 2 ) - 1 );
|
||||
} else {
|
||||
return 17;
|
||||
}
|
||||
}
|
||||
|
||||
extern ssize_t lzx_decompress ( const void *data, size_t len, void *buf );
|
||||
|
||||
#endif /* _LZX_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.c"
|
||||
@@ -0,0 +1,650 @@
|
||||
/******************************************************************************
|
||||
* ventoy.c
|
||||
*
|
||||
* Copyright (c) 2020, longpanda <[email protected]>
|
||||
*
|
||||
* 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; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/kernel.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/memory.h>
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
#include <grub/efi/efi.h>
|
||||
#include <grub/efi/memory.h>
|
||||
#endif
|
||||
#include <grub/ventoy.h>
|
||||
#include "ventoy_def.h"
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
int g_ventoy_debug = 0;
|
||||
static int g_efi_os = 0xFF;
|
||||
grub_uint32_t g_ventoy_plat_data;
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
static VTOY_SHIM *g_vtoy_shim = NULL;
|
||||
#endif
|
||||
|
||||
void ventoy_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start (args, fmt);
|
||||
grub_vprintf (fmt, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
void ventoy_str_tolower(char *str)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
*str = grub_tolower(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
void ventoy_str_toupper(char *str)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
*str = grub_toupper(*str);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
char *ventoy_str_last(char *str, char ch)
|
||||
{
|
||||
char *pos = NULL;
|
||||
char *last = NULL;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (pos = str; *pos; pos++)
|
||||
{
|
||||
if (*pos == ch)
|
||||
{
|
||||
last = pos;
|
||||
}
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
int ventoy_str_all_digit(const char *str)
|
||||
{
|
||||
if (NULL == str || 0 == *str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*str)
|
||||
{
|
||||
if (*str < '0' || *str > '9')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ventoy_str_all_alnum(const char *str)
|
||||
{
|
||||
if (NULL == str || 0 == *str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*str)
|
||||
{
|
||||
if (!grub_isalnum(*str))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ventoy_str_len_alnum(const char *str, int len)
|
||||
{
|
||||
int i;
|
||||
int slen;
|
||||
|
||||
if (NULL == str || 0 == *str)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
slen = grub_strlen(str);
|
||||
if (slen <= len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!grub_isalnum(str[i]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (str[len] == 0 || grub_isspace(str[len]))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char * ventoy_str_basename(char *path)
|
||||
{
|
||||
char *pos = NULL;
|
||||
|
||||
pos = grub_strrchr(path, '/');
|
||||
if (pos)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = path;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int ventoy_str_chrcnt(const char *str, char c)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (str)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
if (*str == c)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int ventoy_strcmp(const char *pattern, const char *str)
|
||||
{
|
||||
while (*pattern && *str)
|
||||
{
|
||||
if ((*pattern != *str) && (*pattern != '*'))
|
||||
break;
|
||||
|
||||
pattern++;
|
||||
str++;
|
||||
}
|
||||
|
||||
return (int)(grub_uint8_t)*pattern - (int)(grub_uint8_t)*str;
|
||||
}
|
||||
|
||||
int ventoy_strncmp (const char *pattern, const char *str, grub_size_t n)
|
||||
{
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
while (*pattern && *str && --n)
|
||||
{
|
||||
if ((*pattern != *str) && (*pattern != '*'))
|
||||
break;
|
||||
|
||||
pattern++;
|
||||
str++;
|
||||
}
|
||||
|
||||
return (int)(grub_uint8_t)*pattern - (int)(grub_uint8_t)*str;
|
||||
}
|
||||
|
||||
grub_err_t ventoy_env_int_set(const char *name, int value)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
grub_snprintf(buf, sizeof(buf), "%d", value);
|
||||
return grub_env_set(name, buf);
|
||||
}
|
||||
|
||||
void ventoy_debug_dump_guid(const char *prefix, grub_uint8_t *guid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!g_ventoy_debug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
debug("%s", prefix);
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
grub_printf("%02x ", guid[i]);
|
||||
}
|
||||
grub_printf("\n");
|
||||
}
|
||||
|
||||
int ventoy_is_efi_os(void)
|
||||
{
|
||||
if (g_efi_os > 1)
|
||||
{
|
||||
g_efi_os = (grub_strstr(GRUB_PLATFORM, "efi")) ? 1 : 0;
|
||||
}
|
||||
|
||||
return g_efi_os;
|
||||
}
|
||||
|
||||
void * ventoy_alloc_chain(grub_size_t size)
|
||||
{
|
||||
void *p = NULL;
|
||||
|
||||
p = grub_malloc(size);
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
if (!p)
|
||||
{
|
||||
p = grub_efi_allocate_any_pages(GRUB_EFI_BYTES_TO_PAGES(size));
|
||||
}
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ventoy_memfile_env_set(const char *prefix, const void *buf, unsigned long long len)
|
||||
{
|
||||
char name[128];
|
||||
char val[64];
|
||||
|
||||
grub_snprintf(name, sizeof(name), "%s_addr", prefix);
|
||||
grub_snprintf(val, sizeof(val), "0x%llx", (ulonglong)(ulong)buf);
|
||||
grub_env_set(name, val);
|
||||
|
||||
grub_snprintf(name, sizeof(name), "%s_size", prefix);
|
||||
grub_snprintf(val, sizeof(val), "%llu", len);
|
||||
grub_env_set(name, val);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
static void ventoy_get_uefi_version(char *str, grub_size_t len)
|
||||
{
|
||||
grub_efi_uint8_t uefi_minor_1, uefi_minor_2;
|
||||
|
||||
uefi_minor_1 = (grub_efi_system_table->hdr.revision & 0xffff) / 10;
|
||||
uefi_minor_2 = (grub_efi_system_table->hdr.revision & 0xffff) % 10;
|
||||
grub_snprintf(str, len, "%d.%d", (grub_efi_system_table->hdr.revision >> 16), uefi_minor_1);
|
||||
if (uefi_minor_2)
|
||||
grub_snprintf(str, len, "%s.%d", str, uefi_minor_2);
|
||||
}
|
||||
|
||||
static void ventoy_get_pi_version(char *str, grub_size_t len)
|
||||
{
|
||||
grub_uint32_t data = 0;
|
||||
grub_efi_uintn_t i = 0;
|
||||
grub_efi_guid_t dxest =
|
||||
{ 0x05ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } };
|
||||
|
||||
for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
|
||||
{
|
||||
if (grub_memcmp(&dxest, &grub_efi_system_table->configuration_table[i].vendor_guid, 16) == 0)
|
||||
{
|
||||
grub_memcpy(&data, (char *)grub_efi_system_table->configuration_table[i].vendor_table + 8, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
grub_snprintf(str, len, "%d.%d", (data >> 16) & 0xFFFF, (data & 0xFFFF) / 10);
|
||||
}
|
||||
|
||||
int ventoy_set_sb_policy(void)
|
||||
{
|
||||
const char *env = NULL;
|
||||
static int set_once = 0;
|
||||
|
||||
/* no need when SecureBoot is disabled */
|
||||
if (g_sys_sb == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* can only set once */
|
||||
if (set_once > 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
set_once = 1;
|
||||
|
||||
/* VTOY_SECURE_BOOT_POLICY only take affect once during init */
|
||||
env = grub_env_get("VTOY_SECURE_BOOT_POLICY");
|
||||
if (env)
|
||||
{
|
||||
g_sb_policy = (grub_uint8_t)(env[0] - '0');
|
||||
}
|
||||
|
||||
if (g_sb_policy == VTOY_SB_POLICY_BYPASS)
|
||||
{
|
||||
if (g_vtoy_shim && g_vtoy_shim->ByPassSB)
|
||||
{
|
||||
g_vtoy_shim->ByPassSB();
|
||||
}
|
||||
}
|
||||
else if (g_sb_policy == VTOY_SB_POLICY_CHECK)
|
||||
{
|
||||
if (g_vtoy_shim && g_vtoy_shim->CheckSB)
|
||||
{
|
||||
g_vtoy_shim->CheckSB();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ventoy_get_uefi_sb(void)
|
||||
{
|
||||
grub_uint8_t secure_boot = 0;
|
||||
grub_uint8_t setup_mode = 0;
|
||||
grub_uint8_t *var = NULL;
|
||||
grub_size_t size = 0;
|
||||
grub_efi_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID;
|
||||
|
||||
var = grub_efi_get_variable("SecureBoot", &global, &size);
|
||||
if (var && size == 1 && *var == 1)
|
||||
{
|
||||
secure_boot = 1;
|
||||
}
|
||||
grub_check_free(var);
|
||||
|
||||
size = 0;
|
||||
var = grub_efi_get_variable("SetupMode", &global, &size);
|
||||
if (var && size == 1 && *var == 1)
|
||||
{
|
||||
setup_mode = 1;
|
||||
}
|
||||
|
||||
if (secure_boot == 1 && setup_mode == 0)
|
||||
{
|
||||
g_sys_sb = 1;
|
||||
}
|
||||
|
||||
grub_check_free(var);
|
||||
}
|
||||
|
||||
static int ventoy_secure_boot_init(void)
|
||||
{
|
||||
grub_efi_guid_t ProtGuid = VTOY_SHIM_POLICY_GUID;
|
||||
|
||||
ventoy_get_uefi_sb();
|
||||
|
||||
if (g_sys_sb == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (g_ventoy_plat_data == VTOY_PLAT_X86_64_UEFI)
|
||||
{
|
||||
g_vtoy_shim = grub_efi_locate_protocol(&ProtGuid, NULL);
|
||||
if (g_vtoy_shim == NULL || g_vtoy_shim->ByPassSB == NULL ||
|
||||
g_vtoy_shim->CheckSB == NULL || g_vtoy_shim->Launched == NULL)
|
||||
{
|
||||
/*
|
||||
* Generally when SecureBoot enabled, Ventoy grub must be launched by Ventoy Shim.
|
||||
* But there are some exceptions:
|
||||
* 1. Ventoy key was enrolled directly to the UEFI DB
|
||||
* 2. Some UEFI firmware (MSI) has Image Execution Policy as Always Execute which
|
||||
* means Secure Boot is effectively disabled.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
g_vtoy_shim->Launched();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
static int ventoy_arch_mode_init(void)
|
||||
{
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
|
||||
if (grub_strcmp(GRUB_TARGET_CPU, "i386") == 0)
|
||||
{
|
||||
g_ventoy_plat_data = VTOY_PLAT_I386_UEFI;
|
||||
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "ia32");
|
||||
}
|
||||
else if (grub_strcmp(GRUB_TARGET_CPU, "arm64") == 0)
|
||||
{
|
||||
g_ventoy_plat_data = VTOY_PLAT_ARM64_UEFI;
|
||||
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "aa64");
|
||||
}
|
||||
else if (grub_strcmp(GRUB_TARGET_CPU, "mips64el") == 0)
|
||||
{
|
||||
g_ventoy_plat_data = VTOY_PLAT_MIPS_UEFI;
|
||||
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "mips");
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ventoy_plat_data = VTOY_PLAT_X86_64_UEFI;
|
||||
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "uefi");
|
||||
}
|
||||
#else
|
||||
g_ventoy_plat_data = VTOY_PLAT_X86_LEGACY;
|
||||
grub_snprintf(g_arch_mode_suffix, sizeof(g_arch_mode_suffix), "%s", "legacy");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_calc_totalmem(grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, void *data)
|
||||
{
|
||||
grub_uint64_t *total_mem = (grub_uint64_t *)data;
|
||||
|
||||
(void)addr;
|
||||
(void)type;
|
||||
|
||||
*total_mem += size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_hwinfo_init(void)
|
||||
{
|
||||
char str[256];
|
||||
grub_uint64_t total_mem = 0;
|
||||
|
||||
grub_machine_mmap_iterate(ventoy_calc_totalmem, &total_mem);
|
||||
|
||||
grub_snprintf(str, sizeof(str), "%ld", (long)(total_mem / VTOY_SIZE_1MB));
|
||||
ventoy_env_export("grub_total_ram", str);
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
ventoy_get_uefi_version(str, sizeof(str));
|
||||
ventoy_env_export("grub_uefi_version", str);
|
||||
|
||||
ventoy_get_pi_version(str, sizeof(str));
|
||||
ventoy_env_export("grub_pi_version", str);
|
||||
#else
|
||||
ventoy_env_export("grub_uefi_version", "NA");
|
||||
ventoy_env_export("grub_pi_version", "NA");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static global_var_cfg g_global_vars[] =
|
||||
{
|
||||
{ "gfxmode", "1024x768", NULL },
|
||||
{ ventoy_left_key, "5%", NULL },
|
||||
{ ventoy_top_key, "95%", NULL },
|
||||
{ ventoy_color_key, "#0000ff", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static const char * ventoy_global_var_read_hook(struct grub_env_var *var, const char *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; g_global_vars[i].name; i++)
|
||||
{
|
||||
if (grub_strcmp(g_global_vars[i].name, var->name) == 0)
|
||||
{
|
||||
return g_global_vars[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static char * ventoy_global_var_write_hook(struct grub_env_var *var, const char *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; g_global_vars[i].name; i++)
|
||||
{
|
||||
if (grub_strcmp(g_global_vars[i].name, var->name) == 0)
|
||||
{
|
||||
grub_check_free(g_global_vars[i].value);
|
||||
g_global_vars[i].value = grub_strdup(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return grub_strdup(val);
|
||||
}
|
||||
|
||||
int ventoy_global_var_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; g_global_vars[i].name; i++)
|
||||
{
|
||||
g_global_vars[i].value = grub_strdup(g_global_vars[i].defval);
|
||||
ventoy_env_export(g_global_vars[i].name, g_global_vars[i].defval);
|
||||
grub_register_variable_hook(g_global_vars[i].name, ventoy_global_var_read_hook, ventoy_global_var_write_hook);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ctrl_var_cfg g_ctrl_vars[] =
|
||||
{
|
||||
{ "VTOY_WIN11_BYPASS_CHECK", "1" },
|
||||
{ "VTOY_WIN11_BYPASS_NRO", "1" },
|
||||
{ "VTOY_LINUX_REMOUNT", "0" },
|
||||
{ "VTOY_SECONDARY_BOOT_MENU", "1" },
|
||||
{ "VTOY_WIN_UEFI_RES_LOCK", "3" },
|
||||
{ NULL, "" }
|
||||
};
|
||||
|
||||
static const char * ventoy_ctrl_var_read_hook(struct grub_env_var *var, const char *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; g_ctrl_vars[i].name; i++)
|
||||
{
|
||||
if (grub_strcmp(g_ctrl_vars[i].name, var->name) == 0)
|
||||
{
|
||||
return g_ctrl_vars[i].szval;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static char * ventoy_ctrl_var_write_hook(struct grub_env_var *var, const char *val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; g_ctrl_vars[i].name; i++)
|
||||
{
|
||||
if (grub_strcmp(g_ctrl_vars[i].name, var->name) == 0)
|
||||
{
|
||||
if (val && grub_isdigit(val[0]) && val[1] == 0)
|
||||
{
|
||||
g_ctrl_vars[i].szval[0] = val[0];
|
||||
return grub_strdup(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ctrl_vars[i].szval[0] = '0';
|
||||
return grub_strdup("0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grub_strdup(val);
|
||||
}
|
||||
|
||||
int ventoy_ctrl_var_init(void)
|
||||
{
|
||||
ctrl_var_cfg *cfg = g_ctrl_vars;
|
||||
|
||||
while (cfg->name)
|
||||
{
|
||||
ventoy_env_export(cfg->name, cfg->szval);
|
||||
grub_register_variable_hook(cfg->name, ventoy_ctrl_var_read_hook, ventoy_ctrl_var_write_hook);
|
||||
cfg++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
GRUB_MOD_INIT(ventoy)
|
||||
{
|
||||
ventoy_hwinfo_init();
|
||||
ventoy_env_init();
|
||||
ventoy_arch_mode_init();
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
ventoy_secure_boot_init();
|
||||
#endif
|
||||
|
||||
ventoy_register_all_cmd();
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(ventoy)
|
||||
{
|
||||
ventoy_unregister_all_cmd();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,652 @@
|
||||
/******************************************************************************
|
||||
* ventoy_browser.c
|
||||
*
|
||||
* Copyright (c) 2022, longpanda <[email protected]>
|
||||
*
|
||||
* 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; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/ventoy.h>
|
||||
#include "ventoy_def.h"
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
#define BROWSER_MENU_BUF 65536
|
||||
|
||||
static const char *g_vtoy_dev = NULL;
|
||||
static grub_fs_t g_menu_fs = NULL;
|
||||
static char *g_menu_device = NULL;
|
||||
static grub_device_t g_menu_dev = NULL;
|
||||
static char g_menu_path_buf[1024];
|
||||
static int g_menu_path_len = 0;
|
||||
static browser_node *g_browser_list = NULL;
|
||||
|
||||
static int ventoy_browser_strcmp(char *str1, char *str2)
|
||||
{
|
||||
char *s1, *s2;
|
||||
int c1 = 0;
|
||||
int c2 = 0;
|
||||
|
||||
for (s1 = str1, s2 = str2; *s1 && *s2; s1++, s2++)
|
||||
{
|
||||
c1 = *s1;
|
||||
c2 = *s2;
|
||||
|
||||
if (0 == g_sort_case_sensitive)
|
||||
{
|
||||
if (grub_islower(c1))
|
||||
{
|
||||
c1 = c1 - 'a' + 'A';
|
||||
}
|
||||
|
||||
if (grub_islower(c2))
|
||||
{
|
||||
c2 = c2 - 'a' + 'A';
|
||||
}
|
||||
}
|
||||
|
||||
if (c1 != c2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (c1 - c2);
|
||||
}
|
||||
|
||||
static int ventoy_browser_mbuf_alloc(browser_mbuf *mbuf)
|
||||
{
|
||||
grub_memset(mbuf, 0, sizeof(browser_mbuf));
|
||||
mbuf->buf = grub_malloc(BROWSER_MENU_BUF);
|
||||
if (!mbuf->buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
mbuf->pos = 0;
|
||||
mbuf->max = BROWSER_MENU_BUF;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void ventoy_browser_mbuf_free(browser_mbuf *mbuf)
|
||||
{
|
||||
if (mbuf)
|
||||
grub_check_free(mbuf->buf)
|
||||
}
|
||||
|
||||
static inline int ventoy_browser_mbuf_extend(browser_mbuf *mbuf)
|
||||
{
|
||||
if (mbuf->max - mbuf->pos <= VTOY_SIZE_1KB)
|
||||
{
|
||||
mbuf->max += BROWSER_MENU_BUF;
|
||||
mbuf->buf = grub_realloc(mbuf->buf, mbuf->max);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static browser_node * ventoy_browser_find_top_node(int dir)
|
||||
{
|
||||
browser_node *node = NULL;
|
||||
browser_node *sel = NULL;
|
||||
|
||||
for (node = g_browser_list; node; node = node->next)
|
||||
{
|
||||
if (node->dir == dir)
|
||||
{
|
||||
if (sel)
|
||||
{
|
||||
if (ventoy_browser_strcmp(sel->filename, node->filename) > 0)
|
||||
{
|
||||
sel = node;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sel = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
static int ventoy_browser_iterate_partition(struct grub_disk *disk, const grub_partition_t partition, void *data)
|
||||
{
|
||||
char partname[64];
|
||||
char title[256];
|
||||
grub_device_t dev;
|
||||
grub_fs_t fs;
|
||||
char *Label = NULL;
|
||||
browser_mbuf *mbuf = (browser_mbuf *)data;
|
||||
|
||||
(void)data;
|
||||
|
||||
if (partition->number == 1 && g_vtoy_dev && grub_strcmp(disk->name, g_vtoy_dev) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_snprintf(partname, sizeof(partname) - 1, "%s,%d", disk->name, partition->number + 1);
|
||||
|
||||
dev = grub_device_open(partname);
|
||||
if (!dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs = grub_fs_probe(dev);
|
||||
if (!fs)
|
||||
{
|
||||
grub_device_close(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fs->fs_label(dev, &Label);
|
||||
|
||||
if (ventoy_check_file_exist("(%s)/.ventoyignore", partname))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
grub_snprintf(title, sizeof(title), "%-10s (%s,%s%d) [%s] %s %s",
|
||||
"DISK", disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
|
||||
partition->number + 1, (Label ? Label : ""), fs->name,
|
||||
grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_snprintf(title, sizeof(title), "(%s,%s%d) [%s] %s %s",
|
||||
disk->name, partition->msdostype == 0xee ? "gpt" : "msdos",
|
||||
partition->number + 1, (Label ? Label : ""), fs->name,
|
||||
grub_get_human_size(partition->len << disk->log_sector_size, GRUB_HUMAN_SIZE_SHORT));
|
||||
}
|
||||
|
||||
if (ventoy_get_fs_type(fs->name) >= ventoy_fs_max)
|
||||
{
|
||||
browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
|
||||
" echo \"unsupported file system type!\" \n"
|
||||
" ventoy_pause\n"
|
||||
"}\n",
|
||||
title);
|
||||
}
|
||||
else
|
||||
{
|
||||
browser_ssprintf(mbuf, "menuentry \"%s\" --class=vtoydisk {\n"
|
||||
" vt_browser_dir %s,%d 0x%lx /\n"
|
||||
"}\n",
|
||||
title, disk->name, partition->number + 1, (ulong)fs);
|
||||
}
|
||||
|
||||
ventoy_browser_mbuf_extend(mbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_browser_iterate_disk(const char *name, void *data)
|
||||
{
|
||||
grub_disk_t disk;
|
||||
|
||||
if (name[0] != 'h')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
disk = grub_disk_open(name);
|
||||
if (disk)
|
||||
{
|
||||
grub_partition_iterate(disk, ventoy_browser_iterate_partition, data);
|
||||
grub_disk_close(disk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_browser_valid_dirname(const char *name, int len)
|
||||
{
|
||||
if ((len == 1 && name[0] == '.') ||
|
||||
(len == 2 && name[0] == '.' && name[1] == '.'))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ventoy_img_name_valid(name, len))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_filt_trash_dir)
|
||||
{
|
||||
if (0 == grub_strncmp(name, ".trash-", 7) ||
|
||||
0 == grub_strcmp(name, ".Trashes"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (name[0] == '$')
|
||||
{
|
||||
if (0 == grub_strncmp(name, "$RECYCLE.BIN", 12) ||
|
||||
0 == grub_strncasecmp(name, "$Extend", 7))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (len == 25 && grub_strncmp(name, "System Volume Information", 25) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ventoy_browser_valid_filename(const char *filename, int len, int *type)
|
||||
{
|
||||
if (len < 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FILE_FLT(ISO) && 0 == grub_strcasecmp(filename + len - 4, ".iso"))
|
||||
{
|
||||
*type = img_type_iso;
|
||||
}
|
||||
else if (FILE_FLT(WIM) && g_wimboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".wim")))
|
||||
{
|
||||
*type = img_type_wim;
|
||||
}
|
||||
else if (FILE_FLT(VHD) && g_vhdboot_enable && (0 == grub_strcasecmp(filename + len - 4, ".vhd") ||
|
||||
(len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vhdx"))))
|
||||
{
|
||||
*type = img_type_vhd;
|
||||
}
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
else if (FILE_FLT(EFI) && 0 == grub_strcasecmp(filename + len - 4, ".efi"))
|
||||
{
|
||||
*type = img_type_efi;
|
||||
}
|
||||
#endif
|
||||
else if (FILE_FLT(IMG) && 0 == grub_strcasecmp(filename + len - 4, ".img"))
|
||||
{
|
||||
if (len == 18 && grub_strncmp(filename, "ventoy_", 7) == 0)
|
||||
{
|
||||
if (grub_strncmp(filename + 7, "wimboot", 7) == 0 ||
|
||||
grub_strncmp(filename + 7, "vhdboot", 7) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*type = img_type_img;
|
||||
}
|
||||
else if (FILE_FLT(VTOY) && len >= 5 && 0 == grub_strcasecmp(filename + len - 5, ".vtoy"))
|
||||
{
|
||||
*type = img_type_vtoy;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_filt_dot_underscore_file && filename[0] == '.' && filename[1] == '_')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ventoy_browser_check_ignore(const char *device, const char *root, const char *dir)
|
||||
{
|
||||
grub_file_t file;
|
||||
char fullpath[1024] = {0};
|
||||
|
||||
grub_snprintf(fullpath, 1023, "(%s)%s/%s/.ventoyignore", device, root, dir);
|
||||
file = grub_file_open(fullpath, GRUB_FILE_TYPE_NONE);
|
||||
if (!file)
|
||||
{
|
||||
grub_errno = 0;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_file_close(file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int ventoy_browser_iterate_dir(const char *filename, const struct grub_dirhook_info *info, void *data)
|
||||
{
|
||||
int type;
|
||||
int len;
|
||||
browser_node *node;
|
||||
|
||||
(void)data;
|
||||
|
||||
len = grub_strlen(filename);
|
||||
|
||||
if (info->dir)
|
||||
{
|
||||
if (!ventoy_browser_valid_dirname(filename, len))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ventoy_browser_check_ignore(g_menu_device, g_menu_path_buf, filename))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = grub_zalloc(sizeof(browser_node));
|
||||
if (!node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
node->dir = 1;
|
||||
grub_strncpy(node->filename, filename, sizeof(node->filename));
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
grub_snprintf(node->menuentry, sizeof(node->menuentry),
|
||||
"menuentry \"%-10s [%s]\" --class=vtoydir {\n"
|
||||
" vt_browser_dir %s 0x%lx \"%s/%s\"\n"
|
||||
"}\n",
|
||||
"DIR", filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_snprintf(node->menuentry, sizeof(node->menuentry),
|
||||
"menuentry \"[%s]\" --class=vtoydir {\n"
|
||||
" vt_browser_dir %s 0x%lx \"%s/%s\"\n"
|
||||
"}\n",
|
||||
filename, g_menu_device, (ulong)g_menu_fs, g_menu_path_buf, filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_uint64_t fsize = info->size;
|
||||
|
||||
if (!ventoy_browser_valid_filename(filename, len, &type))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (grub_file_is_vlnk_suffix(filename, len))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = grub_zalloc(sizeof(browser_node));
|
||||
if (!node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fsize == 0)
|
||||
{
|
||||
struct grub_file file;
|
||||
|
||||
grub_memset(&file, 0, sizeof(file));
|
||||
file.device = g_menu_dev;
|
||||
grub_snprintf(node->menuentry, sizeof(node->menuentry), "%s/%s", g_menu_path_buf, filename);
|
||||
if (g_menu_fs->fs_open(&file, node->menuentry) == GRUB_ERR_NONE)
|
||||
{
|
||||
fsize = file.size;
|
||||
g_menu_fs->fs_close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
node->dir = 0;
|
||||
grub_strncpy(node->filename, filename, sizeof(node->filename));
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
grub_snprintf(node->menuentry, sizeof(node->menuentry),
|
||||
"menuentry \"%-10s %s\" --class=%s {\n"
|
||||
" vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
|
||||
" %s_common_menuentry\n"
|
||||
" vt_reset_fake_vlnk\n"
|
||||
"}\n",
|
||||
grub_get_human_size(fsize, GRUB_HUMAN_SIZE_SHORT), filename, g_menu_class[type],
|
||||
g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
|
||||
g_menu_prefix[type]);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_snprintf(node->menuentry, sizeof(node->menuentry),
|
||||
"menuentry \"%s\" --class=%s {\n"
|
||||
" vt_set_fake_vlnk \"(%s)%s/%s\" %s %llu\n"
|
||||
" %s_common_menuentry\n"
|
||||
" vt_reset_fake_vlnk\n"
|
||||
"}\n",
|
||||
filename, g_menu_class[type],
|
||||
g_menu_device, g_menu_path_buf, filename, g_menu_prefix[type], (ulonglong)fsize,
|
||||
g_menu_prefix[type]);
|
||||
}
|
||||
}
|
||||
|
||||
node->prev = NULL;
|
||||
node->next = g_browser_list;
|
||||
if (g_browser_list)
|
||||
{
|
||||
g_browser_list->prev = node;
|
||||
}
|
||||
g_browser_list = node;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t ventoy_browser_iso_part(void)
|
||||
{
|
||||
char cfgfile[64];
|
||||
char *buffer = NULL;
|
||||
int pos = 0;
|
||||
int buflen = 0;
|
||||
int cfglen = 0;
|
||||
|
||||
cfglen = g_tree_script_pos - g_tree_script_pre;
|
||||
buflen = cfglen + 512;
|
||||
buffer = grub_malloc(buflen);
|
||||
if (!buffer)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
pos = grub_snprintf(buffer, buflen, "menuentry \"%-10s [../]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n", "<--");
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = grub_snprintf(buffer, buflen, "menuentry \"[../]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n");
|
||||
}
|
||||
|
||||
grub_memcpy(buffer + pos, g_tree_script_buf + g_tree_script_pre, cfglen);
|
||||
pos += cfglen;
|
||||
|
||||
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)buffer, pos);
|
||||
grub_script_execute_sourcecode(cfgfile);
|
||||
|
||||
grub_free(buffer);
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_browser_dir(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
int i;
|
||||
grub_fs_t fs;
|
||||
grub_device_t dev;
|
||||
char cfgfile[64];
|
||||
browser_node *node;
|
||||
browser_mbuf mbuf;
|
||||
|
||||
(void)ctxt;
|
||||
(void)argc;
|
||||
|
||||
if (args[2][0] == '/' && args[2][1] == 0)
|
||||
{
|
||||
grub_snprintf(cfgfile, sizeof(cfgfile), "(%s)", args[0]);
|
||||
if (grub_strcmp(cfgfile, g_iso_path) == 0)
|
||||
{
|
||||
return ventoy_browser_iso_part();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ventoy_browser_mbuf_alloc(&mbuf))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs = (grub_fs_t)grub_strtoul(args[1], NULL, 16);
|
||||
if (!fs)
|
||||
{
|
||||
debug("Invalid fs %s\n", args[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dev = grub_device_open(args[0]);
|
||||
if (!dev)
|
||||
{
|
||||
debug("Failed to open device %s\n", args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_menu_fs = fs;
|
||||
g_menu_device = args[0];
|
||||
g_menu_dev = dev;
|
||||
g_browser_list = NULL;
|
||||
|
||||
if (args[2][0] == '/' && args[2][1] == 0)
|
||||
{
|
||||
g_menu_path_len = 0;
|
||||
g_menu_path_buf[0] = 0;
|
||||
fs->fs_dir(dev, "/", ventoy_browser_iterate_dir, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_menu_path_len = grub_snprintf(g_menu_path_buf, sizeof(g_menu_path_buf), "%s", args[2]);
|
||||
fs->fs_dir(dev, g_menu_path_buf, ventoy_browser_iterate_dir, NULL);
|
||||
}
|
||||
grub_device_close(dev);
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
browser_ssprintf(&mbuf, "menuentry \"%-10s [(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n", "<--", args[0], g_menu_path_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
browser_ssprintf(&mbuf, "menuentry \"[(%s)%s/..]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n", args[0], g_menu_path_buf);
|
||||
}
|
||||
|
||||
for (i = 1; i >= 0; i--)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
node = ventoy_browser_find_top_node(i);
|
||||
if (node)
|
||||
{
|
||||
browser_ssprintf(&mbuf, "%s", node->menuentry);
|
||||
ventoy_browser_mbuf_extend(&mbuf);
|
||||
|
||||
if (node->prev)
|
||||
{
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
if (node->next)
|
||||
{
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
|
||||
if (node == g_browser_list)
|
||||
{
|
||||
g_browser_list = node->next;
|
||||
}
|
||||
grub_free(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_browser_list = NULL;
|
||||
|
||||
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
|
||||
grub_script_execute_sourcecode(cfgfile);
|
||||
|
||||
ventoy_browser_mbuf_free(&mbuf);
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_browser_disk(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
char cfgfile[64];
|
||||
browser_mbuf mbuf;
|
||||
|
||||
(void)ctxt;
|
||||
(void)argc;
|
||||
(void)args;
|
||||
|
||||
if (!ventoy_browser_mbuf_alloc(&mbuf))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_vtoy_dev = grub_env_get("vtoydev");
|
||||
|
||||
if (g_tree_view_menu_style == 0)
|
||||
{
|
||||
browser_ssprintf(&mbuf, "menuentry \"%-10s [%s]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n", "<--",
|
||||
ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
|
||||
}
|
||||
else
|
||||
{
|
||||
browser_ssprintf(&mbuf, "menuentry \"[%s]\" --class=\"vtoyret\" VTOY_RET {\n "
|
||||
" echo 'return ...' \n}\n",
|
||||
ventoy_get_vmenu_title("VTLANG_BROWER_RETURN"));
|
||||
}
|
||||
|
||||
grub_disk_dev_iterate(ventoy_browser_iterate_disk, &mbuf);
|
||||
|
||||
grub_snprintf(cfgfile, sizeof(cfgfile), "configfile mem:0x%lx:size:%d", (ulong)mbuf.buf, mbuf.pos);
|
||||
grub_script_execute_sourcecode(cfgfile);
|
||||
|
||||
ventoy_browser_mbuf_free(&mbuf);
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,758 @@
|
||||
/******************************************************************************
|
||||
* ventoy_json.c
|
||||
*
|
||||
* Copyright (c) 2020, longpanda <[email protected]>
|
||||
*
|
||||
* 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; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/ventoy.h>
|
||||
#include "ventoy_def.h"
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
static void json_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (g_ventoy_debug == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
va_start (args, fmt);
|
||||
grub_vprintf (fmt, args);
|
||||
va_end (args);
|
||||
|
||||
grub_printf("\n");
|
||||
}
|
||||
|
||||
static void vtoy_json_free(VTOY_JSON *pstJsonHead)
|
||||
{
|
||||
VTOY_JSON *pstNext = NULL;
|
||||
|
||||
while (NULL != pstJsonHead)
|
||||
{
|
||||
pstNext = pstJsonHead->pstNext;
|
||||
if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))
|
||||
{
|
||||
vtoy_json_free(pstJsonHead->pstChild);
|
||||
}
|
||||
|
||||
grub_free(pstJsonHead);
|
||||
pstJsonHead = pstNext;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static char *vtoy_json_skip(const char *pcData)
|
||||
{
|
||||
while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))
|
||||
{
|
||||
pcData++;
|
||||
}
|
||||
|
||||
return (char *)pcData;
|
||||
}
|
||||
|
||||
VTOY_JSON *vtoy_json_find_item
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
JSON_TYPE enDataType,
|
||||
const char *szKey
|
||||
)
|
||||
{
|
||||
while (NULL != pstJson)
|
||||
{
|
||||
if ((enDataType == pstJson->enDataType) &&
|
||||
(0 == grub_strcmp(szKey, pstJson->pcName)))
|
||||
{
|
||||
return pstJson;
|
||||
}
|
||||
pstJson = pstJson->pstNext;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int vtoy_json_parse_number
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *pcData,
|
||||
const char **ppcEnd
|
||||
)
|
||||
{
|
||||
unsigned long Value;
|
||||
|
||||
Value = grub_strtoul(pcData, (char **)ppcEnd, 10);
|
||||
if (*ppcEnd == pcData)
|
||||
{
|
||||
json_debug("Failed to parse json number %s.", pcData);
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pstJson->enDataType = JSON_TYPE_NUMBER;
|
||||
pstJson->unData.lValue = Value;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
static int vtoy_json_parse_string
|
||||
(
|
||||
char *pcNewStart,
|
||||
char *pcRawStart,
|
||||
VTOY_JSON *pstJson,
|
||||
const char *pcData,
|
||||
const char **ppcEnd
|
||||
)
|
||||
{
|
||||
grub_uint32_t uiLen = 0;
|
||||
const char *pcPos = NULL;
|
||||
const char *pcTmp = pcData + 1;
|
||||
|
||||
*ppcEnd = pcData;
|
||||
|
||||
if ('\"' != *pcData)
|
||||
{
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcPos = grub_strchr(pcTmp, '\"');
|
||||
if ((NULL == pcPos) || (pcPos < pcTmp))
|
||||
{
|
||||
json_debug("Invalid string %s.", pcData);
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
if (*(pcPos - 1) == '\\')
|
||||
{
|
||||
for (pcPos++; *pcPos; pcPos++)
|
||||
{
|
||||
if (*pcPos == '"' && *(pcPos - 1) != '\\')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pcPos == 0 || pcPos < pcTmp)
|
||||
{
|
||||
json_debug("Invalid quotes string %s.", pcData);
|
||||
return JSON_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
*ppcEnd = pcPos + 1;
|
||||
uiLen = (grub_uint32_t)(unsigned long)(pcPos - pcTmp);
|
||||
|
||||
pstJson->enDataType = JSON_TYPE_STRING;
|
||||
pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);
|
||||
pstJson->unData.pcStrVal[uiLen] = '\0';
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
static int vtoy_json_parse_array
|
||||
(
|
||||
char *pcNewStart,
|
||||
char *pcRawStart,
|
||||
VTOY_JSON *pstJson,
|
||||
const char *pcData,
|
||||
const char **ppcEnd
|
||||
)
|
||||
{
|
||||
int Ret = JSON_SUCCESS;
|
||||
VTOY_JSON *pstJsonChild = NULL;
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
const char *pcTmp = pcData + 1;
|
||||
|
||||
*ppcEnd = pcData;
|
||||
pstJson->enDataType = JSON_TYPE_ARRAY;
|
||||
|
||||
if ('[' != *pcData)
|
||||
{
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcTmp = vtoy_json_skip(pcTmp);
|
||||
|
||||
if (']' == *pcTmp)
|
||||
{
|
||||
*ppcEnd = pcTmp + 1;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
|
||||
|
||||
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pstJsonChild = pstJson->pstChild;
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
while ((NULL != pcTmp) && (',' == *pcTmp))
|
||||
{
|
||||
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
|
||||
pstJsonChild->pstNext = pstJsonItem;
|
||||
pstJsonItem->pstPrev = pstJsonChild;
|
||||
pstJsonChild = pstJsonItem;
|
||||
|
||||
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
}
|
||||
|
||||
if ((NULL != pcTmp) && (']' == *pcTmp))
|
||||
{
|
||||
*ppcEnd = pcTmp + 1;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppcEnd = pcTmp;
|
||||
return JSON_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
static int vtoy_json_parse_object
|
||||
(
|
||||
char *pcNewStart,
|
||||
char *pcRawStart,
|
||||
VTOY_JSON *pstJson,
|
||||
const char *pcData,
|
||||
const char **ppcEnd
|
||||
)
|
||||
{
|
||||
int Ret = JSON_SUCCESS;
|
||||
VTOY_JSON *pstJsonChild = NULL;
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
const char *pcTmp = pcData + 1;
|
||||
|
||||
*ppcEnd = pcData;
|
||||
pstJson->enDataType = JSON_TYPE_OBJECT;
|
||||
|
||||
if ('{' != *pcData)
|
||||
{
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcTmp = vtoy_json_skip(pcTmp);
|
||||
if ('}' == *pcTmp)
|
||||
{
|
||||
*ppcEnd = pcTmp + 1;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
|
||||
|
||||
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pstJsonChild = pstJson->pstChild;
|
||||
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
|
||||
pstJsonChild->unData.pcStrVal = NULL;
|
||||
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
if ((NULL == pcTmp) || (':' != *pcTmp))
|
||||
{
|
||||
*ppcEnd = pcTmp;
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
while ((NULL != pcTmp) && (',' == *pcTmp))
|
||||
{
|
||||
JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
|
||||
pstJsonChild->pstNext = pstJsonItem;
|
||||
pstJsonItem->pstPrev = pstJsonChild;
|
||||
pstJsonChild = pstJsonItem;
|
||||
|
||||
Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
|
||||
pstJsonChild->unData.pcStrVal = NULL;
|
||||
if ((NULL == pcTmp) || (':' != *pcTmp))
|
||||
{
|
||||
*ppcEnd = pcTmp;
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse array child.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
pcTmp = vtoy_json_skip(*ppcEnd);
|
||||
}
|
||||
|
||||
if ((NULL != pcTmp) && ('}' == *pcTmp))
|
||||
{
|
||||
*ppcEnd = pcTmp + 1;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppcEnd = pcTmp;
|
||||
return JSON_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
int vtoy_json_parse_value
|
||||
(
|
||||
char *pcNewStart,
|
||||
char *pcRawStart,
|
||||
VTOY_JSON *pstJson,
|
||||
const char *pcData,
|
||||
const char **ppcEnd
|
||||
)
|
||||
{
|
||||
pcData = vtoy_json_skip(pcData);
|
||||
|
||||
switch (*pcData)
|
||||
{
|
||||
case 'n':
|
||||
{
|
||||
if (0 == grub_strncmp(pcData, "null", 4))
|
||||
{
|
||||
pstJson->enDataType = JSON_TYPE_NULL;
|
||||
*ppcEnd = pcData + 4;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
if (0 == grub_strncmp(pcData, "false", 5))
|
||||
{
|
||||
pstJson->enDataType = JSON_TYPE_BOOL;
|
||||
pstJson->unData.lValue = 0;
|
||||
*ppcEnd = pcData + 5;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
if (0 == grub_strncmp(pcData, "true", 4))
|
||||
{
|
||||
pstJson->enDataType = JSON_TYPE_BOOL;
|
||||
pstJson->unData.lValue = 1;
|
||||
*ppcEnd = pcData + 4;
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '\"':
|
||||
{
|
||||
return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
|
||||
}
|
||||
case '[':
|
||||
{
|
||||
return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
|
||||
}
|
||||
case '{':
|
||||
{
|
||||
return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
|
||||
}
|
||||
case '-':
|
||||
{
|
||||
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
|
||||
}
|
||||
default :
|
||||
{
|
||||
if (*pcData >= '0' && *pcData <= '9')
|
||||
{
|
||||
return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*ppcEnd = pcData;
|
||||
json_debug("Invalid json data %u.", (grub_uint8_t)(*pcData));
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
VTOY_JSON * vtoy_json_create(void)
|
||||
{
|
||||
VTOY_JSON *pstJson = NULL;
|
||||
|
||||
pstJson = (VTOY_JSON *)grub_zalloc(sizeof(VTOY_JSON));
|
||||
if (NULL == pstJson)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pstJson;
|
||||
}
|
||||
|
||||
int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)
|
||||
{
|
||||
grub_uint32_t uiMemSize = 0;
|
||||
int Ret = JSON_SUCCESS;
|
||||
char *pcNewBuf = NULL;
|
||||
const char *pcEnd = NULL;
|
||||
|
||||
uiMemSize = grub_strlen(szJsonData) + 1;
|
||||
pcNewBuf = (char *)grub_malloc(uiMemSize);
|
||||
if (NULL == pcNewBuf)
|
||||
{
|
||||
json_debug("Failed to alloc new buf.");
|
||||
return JSON_FAILED;
|
||||
}
|
||||
grub_memcpy(pcNewBuf, szJsonData, uiMemSize);
|
||||
pcNewBuf[uiMemSize - 1] = 0;
|
||||
|
||||
Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);
|
||||
if (JSON_SUCCESS != Ret)
|
||||
{
|
||||
json_debug("Failed to parse json data %s start=%p, end=%p:%s.",
|
||||
szJsonData, szJsonData, pcEnd, pcEnd);
|
||||
return JSON_FAILED;
|
||||
}
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_scan_parse
|
||||
(
|
||||
const VTOY_JSON *pstJson,
|
||||
grub_uint32_t uiParseNum,
|
||||
JSON_PARSE *pstJsonParse
|
||||
)
|
||||
{
|
||||
grub_uint32_t i = 0;
|
||||
const VTOY_JSON *pstJsonCur = NULL;
|
||||
JSON_PARSE *pstCurParse = NULL;
|
||||
|
||||
for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)
|
||||
{
|
||||
if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||
|
||||
(JSON_TYPE_ARRAY == pstJsonCur->enDataType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)
|
||||
{
|
||||
if (0 == grub_strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))
|
||||
{
|
||||
pstCurParse = pstJsonParse + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL == pstCurParse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (pstJsonCur->enDataType)
|
||||
{
|
||||
case JSON_TYPE_NUMBER:
|
||||
{
|
||||
if (sizeof(grub_uint32_t) == pstCurParse->uiBufSize)
|
||||
{
|
||||
*(grub_uint32_t *)(pstCurParse->pDataBuf) = (grub_uint32_t)pstJsonCur->unData.lValue;
|
||||
}
|
||||
else if (sizeof(grub_uint16_t) == pstCurParse->uiBufSize)
|
||||
{
|
||||
*(grub_uint16_t *)(pstCurParse->pDataBuf) = (grub_uint16_t)pstJsonCur->unData.lValue;
|
||||
}
|
||||
else if (sizeof(grub_uint8_t) == pstCurParse->uiBufSize)
|
||||
{
|
||||
*(grub_uint8_t *)(pstCurParse->pDataBuf) = (grub_uint8_t)pstJsonCur->unData.lValue;
|
||||
}
|
||||
else if ((pstCurParse->uiBufSize > sizeof(grub_uint64_t)))
|
||||
{
|
||||
grub_snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu",
|
||||
(unsigned long long)(pstJsonCur->unData.lValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
json_debug("Invalid number data buf size %u.", pstCurParse->uiBufSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSON_TYPE_STRING:
|
||||
{
|
||||
grub_strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);
|
||||
break;
|
||||
}
|
||||
case JSON_TYPE_BOOL:
|
||||
{
|
||||
*(grub_uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
default :
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_scan_array
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
VTOY_JSON **ppstArrayItem
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*ppstArrayItem = pstJsonItem;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_scan_array_ex
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
VTOY_JSON **ppstArrayItem
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*ppstArrayItem = pstJsonItem->pstChild;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_scan_object
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
VTOY_JSON **ppstObjectItem
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*ppstObjectItem = pstJsonItem;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_get_int
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
int *piValue
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*piValue = (int)pstJsonItem->unData.lValue;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_get_uint
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
grub_uint32_t *puiValue
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*puiValue = (grub_uint32_t)pstJsonItem->unData.lValue;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_get_uint64
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
grub_uint64_t *pui64Value
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*pui64Value = (grub_uint64_t)pstJsonItem->unData.lValue;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_get_bool
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
grub_uint8_t *pbValue
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
*pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
int vtoy_json_get_string
|
||||
(
|
||||
VTOY_JSON *pstJson,
|
||||
const char *szKey,
|
||||
grub_uint32_t uiBufLen,
|
||||
char *pcBuf
|
||||
)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return JSON_NOT_FOUND;
|
||||
}
|
||||
|
||||
grub_strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson, const char *szKey)
|
||||
{
|
||||
VTOY_JSON *pstJsonItem = NULL;
|
||||
|
||||
if ((NULL == pstJson) || (NULL == szKey))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
|
||||
if (NULL == pstJsonItem)
|
||||
{
|
||||
json_debug("Key %s is not found in json data.", szKey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pstJsonItem->unData.pcStrVal;
|
||||
}
|
||||
|
||||
int vtoy_json_destroy(VTOY_JSON *pstJson)
|
||||
{
|
||||
if (NULL == pstJson)
|
||||
{
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
if (NULL != pstJson->pstChild)
|
||||
{
|
||||
vtoy_json_free(pstJson->pstChild);
|
||||
}
|
||||
|
||||
if (NULL != pstJson->pstNext)
|
||||
{
|
||||
vtoy_json_free(pstJson->pstNext);
|
||||
}
|
||||
|
||||
grub_free(pstJson);
|
||||
|
||||
return JSON_SUCCESS;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,754 @@
|
||||
/******************************************************************************
|
||||
* ventoy_vhd.c
|
||||
*
|
||||
* Copyright (c) 2020, longpanda <[email protected]>
|
||||
*
|
||||
* 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; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/crypto.h>
|
||||
#include <grub/charset.h>
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
#include <grub/efi/efi.h>
|
||||
#endif
|
||||
#include <grub/ventoy.h>
|
||||
#include "ventoy_def.h"
|
||||
|
||||
GRUB_MOD_LICENSE ("GPLv3+");
|
||||
|
||||
static int g_vhdboot_isolen = 0;
|
||||
static char *g_vhdboot_totbuf = NULL;
|
||||
static char *g_vhdboot_isobuf = NULL;
|
||||
static grub_uint64_t g_img_trim_head_secnum = 0;
|
||||
|
||||
static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen, const char *path)
|
||||
{
|
||||
grub_uint32_t offset;
|
||||
grub_file_t file;
|
||||
char cmdbuf[128];
|
||||
|
||||
grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback vhdiso mem:0x%lx:size:%d", (ulong)g_vhdboot_isobuf, g_vhdboot_isolen);
|
||||
|
||||
grub_script_execute_sourcecode(cmdbuf);
|
||||
|
||||
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(vhdiso)%s", path);
|
||||
if (!file)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
grub_file_read(file, &offset, 4);
|
||||
offset = (grub_uint32_t)grub_iso9660_get_last_read_pos(file);
|
||||
|
||||
*bcdoffset = (int)offset;
|
||||
*bcdlen = (int)file->size;
|
||||
|
||||
debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset, *bcdlen);
|
||||
|
||||
grub_file_close(file);
|
||||
|
||||
grub_script_execute_sourcecode("loopback -d vhdiso");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2,
|
||||
int bcdoffset, int bcdlen)
|
||||
{
|
||||
int i;
|
||||
int cnt = 0;
|
||||
char *pos;
|
||||
grub_size_t pathlen;
|
||||
const char *plat;
|
||||
char *newpath = NULL;
|
||||
grub_uint16_t *unicode_path;
|
||||
const grub_uint8_t winloadexe[] =
|
||||
{
|
||||
0x77, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x2E, 0x00,
|
||||
0x65, 0x00, 0x78, 0x00, 0x65, 0x00
|
||||
};
|
||||
|
||||
while ((*vhdpath) != '/')
|
||||
{
|
||||
vhdpath++;
|
||||
}
|
||||
|
||||
pathlen = sizeof(grub_uint16_t) * (grub_strlen(vhdpath) + 1);
|
||||
debug("unicode path for <%s> len:%d\n", vhdpath, (int)pathlen);
|
||||
|
||||
unicode_path = grub_zalloc(pathlen);
|
||||
if (!unicode_path)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
plat = grub_env_get("grub_platform");
|
||||
|
||||
if (plat && (plat[0] == 'e')) /* UEFI */
|
||||
{
|
||||
pos = g_vhdboot_isobuf + bcdoffset;
|
||||
|
||||
/* winload.exe ==> winload.efi */
|
||||
for (i = 0; i + (int)sizeof(winloadexe) < bcdlen; i++)
|
||||
{
|
||||
if (*((grub_uint32_t *)(pos + i)) == 0x00690077 &&
|
||||
grub_memcmp(pos + i, winloadexe, sizeof(winloadexe)) == 0)
|
||||
{
|
||||
pos[i + sizeof(winloadexe) - 4] = 0x66;
|
||||
pos[i + sizeof(winloadexe) - 2] = 0x69;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
debug("winload patch %d times\n", cnt);
|
||||
}
|
||||
|
||||
newpath = grub_strdup(vhdpath);
|
||||
for (pos = newpath; *pos; pos++)
|
||||
{
|
||||
if (*pos == '/')
|
||||
{
|
||||
*pos = '\\';
|
||||
}
|
||||
}
|
||||
|
||||
grub_utf8_to_utf16(unicode_path, pathlen, (grub_uint8_t *)newpath, -1, NULL);
|
||||
grub_memcpy(patch1->vhd_file_path, unicode_path, pathlen);
|
||||
grub_memcpy(patch2->vhd_file_path, unicode_path, pathlen);
|
||||
|
||||
grub_free(newpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_vhd_read_parttbl(const char *filename, ventoy_gpt_info *gpt, int *index, grub_uint64_t *poffset)
|
||||
{
|
||||
int i;
|
||||
int find = 0;
|
||||
int ret = 1;
|
||||
grub_uint64_t start;
|
||||
grub_file_t file = NULL;
|
||||
grub_disk_t disk = NULL;
|
||||
grub_uint8_t zeroguid[16] = {0};
|
||||
|
||||
file = grub_file_open(filename, VENTOY_FILE_TYPE);
|
||||
if (!file)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
disk = grub_disk_open(file->device->disk->name);
|
||||
if (!disk)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
grub_disk_read(disk, 0, 0, sizeof(ventoy_gpt_info), gpt);
|
||||
|
||||
start = file->device->disk->partition->start;
|
||||
|
||||
if (grub_memcmp(gpt->Head.Signature, "EFI PART", 8) == 0)
|
||||
{
|
||||
debug("GPT part start: %llu\n", (ulonglong)start);
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
if (grub_memcmp(gpt->PartTbl[i].PartGuid, zeroguid, 16))
|
||||
{
|
||||
if (start == gpt->PartTbl[i].StartLBA)
|
||||
{
|
||||
*index = i;
|
||||
find = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("MBR part start: %llu\n", (ulonglong)start);
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if ((grub_uint32_t)start == gpt->MBR.PartTbl[i].StartSectorId)
|
||||
{
|
||||
*index = i;
|
||||
find = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (find == 0) // MBR Logical partition
|
||||
{
|
||||
if (file->device->disk->partition->number > 0)
|
||||
{
|
||||
*index = file->device->disk->partition->number;
|
||||
debug("Fall back part number: %d\n", *index);
|
||||
}
|
||||
}
|
||||
|
||||
*poffset = start;
|
||||
ret = 0;
|
||||
|
||||
end:
|
||||
check_free(file, grub_file_close);
|
||||
check_free(disk, grub_disk_close);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ventoy_vhd_patch_disk(const char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2)
|
||||
{
|
||||
int partIndex = 0;
|
||||
grub_uint64_t offset = 0;
|
||||
char efipart[16] = {0};
|
||||
ventoy_gpt_info *gpt = NULL;
|
||||
|
||||
if (vhdpath[0] == '/')
|
||||
{
|
||||
gpt = g_ventoy_part_info;
|
||||
partIndex = 0;
|
||||
debug("This is Ventoy ISO partIndex %d %s\n", partIndex, vhdpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpt = grub_zalloc(sizeof(ventoy_gpt_info));
|
||||
ventoy_vhd_read_parttbl(vhdpath, gpt, &partIndex, &offset);
|
||||
debug("This is HDD partIndex %d %s\n", partIndex, vhdpath);
|
||||
}
|
||||
|
||||
grub_memcpy(efipart, gpt->Head.Signature, sizeof(gpt->Head.Signature));
|
||||
|
||||
grub_memset(patch1, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path));
|
||||
grub_memset(patch2, 0, OFFSET_OF(ventoy_patch_vhd, vhd_file_path));
|
||||
|
||||
if (grub_strncmp(efipart, "EFI PART", 8) == 0)
|
||||
{
|
||||
ventoy_debug_dump_guid("GPT disk GUID: ", gpt->Head.DiskGuid);
|
||||
ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt->PartTbl[partIndex].PartGuid);
|
||||
|
||||
grub_memcpy(patch1->disk_signature_or_guid, gpt->Head.DiskGuid, 16);
|
||||
grub_memcpy(patch1->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16);
|
||||
grub_memcpy(patch2->disk_signature_or_guid, gpt->Head.DiskGuid, 16);
|
||||
grub_memcpy(patch2->part_offset_or_guid, gpt->PartTbl[partIndex].PartGuid, 16);
|
||||
|
||||
patch1->part_type = patch2->part_type = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset == 0)
|
||||
{
|
||||
offset = gpt->MBR.PartTbl[partIndex].StartSectorId;
|
||||
}
|
||||
offset *= 512;
|
||||
debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n",
|
||||
gpt->MBR.BootCode[0x1b8 + 0], gpt->MBR.BootCode[0x1b8 + 1],
|
||||
gpt->MBR.BootCode[0x1b8 + 2], gpt->MBR.BootCode[0x1b8 + 3],
|
||||
partIndex + 1, offset);
|
||||
|
||||
grub_memcpy(patch1->part_offset_or_guid, &offset, 8);
|
||||
grub_memcpy(patch2->part_offset_or_guid, &offset, 8);
|
||||
|
||||
grub_memcpy(patch1->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4);
|
||||
grub_memcpy(patch2->disk_signature_or_guid, gpt->MBR.BootCode + 0x1b8, 4);
|
||||
|
||||
patch1->part_type = patch2->part_type = 1;
|
||||
}
|
||||
|
||||
if (gpt != g_ventoy_part_info)
|
||||
{
|
||||
grub_free(gpt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_find_vhdpatch_offset(int bcdoffset, int bcdlen, int *offset)
|
||||
{
|
||||
int i;
|
||||
int cnt = 0;
|
||||
grub_uint8_t *buf = (grub_uint8_t *)(g_vhdboot_isobuf + bcdoffset);
|
||||
grub_uint8_t magic[16] = {
|
||||
0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00
|
||||
};
|
||||
|
||||
for (i = 0; i < bcdlen - 16 && cnt < 2; i++)
|
||||
{
|
||||
if (*(grub_uint32_t *)(buf + i) == 0x0058005C)
|
||||
{
|
||||
if (grub_memcmp(magic, buf + i, 16) == 0)
|
||||
{
|
||||
*offset++ = i - (int)OFFSET_OF(ventoy_patch_vhd, vhd_file_path);
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
int rc;
|
||||
int bcdoffset, bcdlen;
|
||||
int patchoffset[2];
|
||||
ventoy_patch_vhd *patch1;
|
||||
ventoy_patch_vhd *patch2;
|
||||
|
||||
(void)ctxt;
|
||||
(void)argc;
|
||||
|
||||
grub_env_unset("vtoy_vhd_buf_addr");
|
||||
|
||||
debug("patch vhd <%s>\n", args[0]);
|
||||
|
||||
if ((!g_vhdboot_enable) || (!g_vhdboot_totbuf))
|
||||
{
|
||||
debug("vhd boot not ready %d %p\n", g_vhdboot_enable, g_vhdboot_totbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/bcd");
|
||||
if (rc)
|
||||
{
|
||||
debug("failed to get bcd location %d\n", rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset);
|
||||
patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]);
|
||||
patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]);
|
||||
|
||||
debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
|
||||
bcdoffset, bcdlen, patchoffset[0], patchoffset[1]);
|
||||
ventoy_vhd_patch_disk(args[0], patch1, patch2);
|
||||
ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen);
|
||||
}
|
||||
|
||||
rc = ventoy_vhd_find_bcd(&bcdoffset, &bcdlen, "/boot/BCD");
|
||||
if (rc)
|
||||
{
|
||||
debug("No file /boot/BCD \n");
|
||||
}
|
||||
else
|
||||
{
|
||||
ventoy_find_vhdpatch_offset(bcdoffset, bcdlen, patchoffset);
|
||||
patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[0]);
|
||||
patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + bcdoffset + patchoffset[1]);
|
||||
|
||||
debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
|
||||
bcdoffset, bcdlen, patchoffset[0], patchoffset[1]);
|
||||
ventoy_vhd_patch_disk(args[0], patch1, patch2);
|
||||
ventoy_vhd_patch_path(args[0], patch1, patch2, bcdoffset, bcdlen);
|
||||
}
|
||||
|
||||
/* set buffer and size */
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_totbuf, (ulonglong)(g_vhdboot_isolen + sizeof(ventoy_chain_head)));
|
||||
#else
|
||||
ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_isobuf, (ulonglong)g_vhdboot_isolen);
|
||||
#endif
|
||||
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
int buflen;
|
||||
grub_file_t file;
|
||||
|
||||
(void)ctxt;
|
||||
(void)argc;
|
||||
|
||||
g_vhdboot_enable = 0;
|
||||
grub_check_free(g_vhdboot_totbuf);
|
||||
|
||||
file = grub_file_open(args[0], VENTOY_FILE_TYPE);
|
||||
if (!file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("load vhd boot: <%s> <%lu>\n", args[0], (ulong)file->size);
|
||||
|
||||
if (file->size < VTOY_SIZE_1KB * 32)
|
||||
{
|
||||
grub_file_close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_vhdboot_isolen = (int)file->size;
|
||||
|
||||
buflen = (int)(file->size + sizeof(ventoy_chain_head));
|
||||
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
g_vhdboot_totbuf = (char *)grub_efi_allocate_iso_buf(buflen);
|
||||
#else
|
||||
g_vhdboot_totbuf = (char *)grub_malloc(buflen);
|
||||
#endif
|
||||
|
||||
if (!g_vhdboot_totbuf)
|
||||
{
|
||||
grub_file_close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_vhdboot_isobuf = g_vhdboot_totbuf + sizeof(ventoy_chain_head);
|
||||
|
||||
grub_file_read(file, g_vhdboot_isobuf, file->size);
|
||||
grub_file_close(file);
|
||||
|
||||
g_vhdboot_enable = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ventoy_raw_trim_head(grub_uint64_t offset)
|
||||
{
|
||||
grub_uint32_t i;
|
||||
grub_uint32_t memsize;
|
||||
grub_uint32_t imgstart = 0;
|
||||
grub_uint32_t imgsecs = 0;
|
||||
grub_uint64_t sectors = 0;
|
||||
grub_uint64_t cursecs = 0;
|
||||
grub_uint64_t delta = 0;
|
||||
|
||||
if ((!g_img_chunk_list.chunk) || (!offset))
|
||||
{
|
||||
debug("image chunk not ready %p %lu\n", g_img_chunk_list.chunk, (ulong)offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("image trim head %lu\n", (ulong)offset);
|
||||
|
||||
for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
|
||||
{
|
||||
cursecs = g_img_chunk_list.chunk[i].disk_end_sector + 1 - g_img_chunk_list.chunk[i].disk_start_sector;
|
||||
sectors += cursecs;
|
||||
if (sectors >= offset)
|
||||
{
|
||||
delta = cursecs - (sectors - offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sectors < offset || i >= g_img_chunk_list.cur_chunk)
|
||||
{
|
||||
debug("Invalid size %lu %lu\n", (ulong)sectors, (ulong)offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sectors == offset)
|
||||
{
|
||||
memsize = (g_img_chunk_list.cur_chunk - (i + 1)) * sizeof(ventoy_img_chunk);
|
||||
grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i + 1, memsize);
|
||||
g_img_chunk_list.cur_chunk -= (i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_img_chunk_list.chunk[i].disk_start_sector += delta;
|
||||
g_img_chunk_list.chunk[i].img_start_sector += (grub_uint32_t)(delta / 4);
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
memsize = (g_img_chunk_list.cur_chunk - i) * sizeof(ventoy_img_chunk);
|
||||
grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i, memsize);
|
||||
g_img_chunk_list.cur_chunk -= i;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
|
||||
{
|
||||
imgsecs = g_img_chunk_list.chunk[i].img_end_sector + 1 - g_img_chunk_list.chunk[i].img_start_sector;
|
||||
g_img_chunk_list.chunk[i].img_start_sector = imgstart;
|
||||
g_img_chunk_list.chunk[i].img_end_sector = imgstart + (imgsecs - 1);
|
||||
imgstart += imgsecs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
int i;
|
||||
int altboot = 0;
|
||||
int offset = -1;
|
||||
grub_file_t file;
|
||||
grub_uint8_t data = 0;
|
||||
vhd_footer_t vhdfoot;
|
||||
VDIPREHEADER vdihdr;
|
||||
char type[16] = {0};
|
||||
ventoy_gpt_info *gpt = NULL;
|
||||
|
||||
(void)ctxt;
|
||||
|
||||
g_img_trim_head_secnum = 0;
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
file = grub_file_open(args[0], VENTOY_FILE_TYPE);
|
||||
if (!file)
|
||||
{
|
||||
debug("Failed to open file %s\n", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
grub_snprintf(type, sizeof(type), "unknown");
|
||||
|
||||
grub_file_seek(file, file->size - 512);
|
||||
grub_file_read(file, &vhdfoot, sizeof(vhdfoot));
|
||||
|
||||
if (grub_strncmp(vhdfoot.cookie, "conectix", 8) == 0)
|
||||
{
|
||||
offset = 0;
|
||||
grub_snprintf(type, sizeof(type), "vhd%u", grub_swap_bytes32(vhdfoot.disktype));
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_file_seek(file, 0);
|
||||
grub_file_read(file, &vdihdr, sizeof(vdihdr));
|
||||
if (vdihdr.u32Signature == VDI_IMAGE_SIGNATURE)
|
||||
{
|
||||
grub_snprintf(type, sizeof(type), "vdi");
|
||||
if (grub_strncmp(vdihdr.szFileInfo, VDI_IMAGE_FILE_INFO, grub_strlen(VDI_IMAGE_FILE_INFO)) == 0)
|
||||
{
|
||||
offset = 2 * 1048576;
|
||||
g_img_trim_head_secnum = offset / 512;
|
||||
debug("VDI V1\n");
|
||||
}
|
||||
else if (grub_strncmp(vdihdr.szFileInfo, VDI_IMAGE_FILE_INFO2, grub_strlen(VDI_IMAGE_FILE_INFO2)) == 0)
|
||||
{
|
||||
offset = 2 * 1048576;
|
||||
g_img_trim_head_secnum = offset / 512;
|
||||
debug("VDI V2\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("invalid file info <%s>\n", vdihdr.szFileInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 0;
|
||||
grub_snprintf(type, sizeof(type), "raw");
|
||||
}
|
||||
}
|
||||
|
||||
grub_env_set(args[1], type);
|
||||
debug("<%s> vtoy type: <%s> offset:%d\n", args[0], type, offset);
|
||||
|
||||
if (offset >= 0)
|
||||
{
|
||||
gpt = grub_zalloc(sizeof(ventoy_gpt_info));
|
||||
if (!gpt)
|
||||
{
|
||||
grub_env_set(args[1], "unknown");
|
||||
goto end;
|
||||
}
|
||||
|
||||
grub_file_seek(file, offset);
|
||||
grub_file_read(file, gpt, sizeof(ventoy_gpt_info));
|
||||
|
||||
if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
|
||||
{
|
||||
grub_env_set(args[1], "unknown");
|
||||
debug("invalid mbr signature: 0x%x 0x%x offset=%d\n", gpt->MBR.Byte55, gpt->MBR.ByteAA, offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (grub_memcmp(gpt->Head.Signature, "EFI PART", 8) == 0)
|
||||
{
|
||||
grub_env_set(args[2], "gpt");
|
||||
debug("part type: %s\n", "GPT");
|
||||
|
||||
if (gpt->MBR.PartTbl[0].FsFlag == 0xEE)
|
||||
{
|
||||
for (i = 0; i < 128; i++)
|
||||
{
|
||||
if (grub_memcmp(gpt->PartTbl[i].PartType, "Hah!IdontNeedEFI", 16) == 0)
|
||||
{
|
||||
debug("part %d is grub_bios part\n", i);
|
||||
altboot = 1;
|
||||
grub_env_set(args[3], "1");
|
||||
break;
|
||||
}
|
||||
else if (gpt->PartTbl[i].LastLBA == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!altboot)
|
||||
{
|
||||
if (gpt->MBR.BootCode[92] == 0x22)
|
||||
{
|
||||
grub_file_seek(file, offset + 17908);
|
||||
grub_file_read(file, &data, 1);
|
||||
if (data == 0x23)
|
||||
{
|
||||
altboot = 1;
|
||||
grub_env_set(args[3], "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("offset data=0x%x\n", data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("BootCode: 0x%x\n", gpt->MBR.BootCode[92]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_env_set(args[2], "mbr");
|
||||
debug("part type: %s\n", "MBR");
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if (gpt->MBR.PartTbl[i].FsFlag == 0xEF)
|
||||
{
|
||||
debug("part %d is esp part in MBR mode\n", i);
|
||||
altboot = 1;
|
||||
grub_env_set(args[3], "1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("part type: %s\n", "xxx");
|
||||
}
|
||||
|
||||
end:
|
||||
grub_check_free(gpt);
|
||||
grub_file_close(file);
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
|
||||
grub_err_t ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
|
||||
{
|
||||
grub_uint32_t size = 0;
|
||||
grub_uint32_t img_chunk_size = 0;
|
||||
grub_file_t file;
|
||||
grub_disk_t disk;
|
||||
const char *pLastChain = NULL;
|
||||
ventoy_chain_head *chain;
|
||||
|
||||
(void)ctxt;
|
||||
(void)argc;
|
||||
|
||||
if (NULL == g_img_chunk_list.chunk)
|
||||
{
|
||||
grub_printf("ventoy not ready\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (g_img_trim_head_secnum > 0)
|
||||
{
|
||||
ventoy_raw_trim_head(g_img_trim_head_secnum);
|
||||
}
|
||||
|
||||
file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
|
||||
if (!file)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (grub_strncmp(args[0], g_iso_path, grub_strlen(g_iso_path)))
|
||||
{
|
||||
file->vlnk = 1;
|
||||
}
|
||||
|
||||
img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
|
||||
|
||||
size = sizeof(ventoy_chain_head) + img_chunk_size;
|
||||
|
||||
pLastChain = grub_env_get("vtoy_chain_mem_addr");
|
||||
if (pLastChain)
|
||||
{
|
||||
chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
|
||||
if (chain)
|
||||
{
|
||||
debug("free last chain memory %p\n", chain);
|
||||
grub_free(chain);
|
||||
}
|
||||
}
|
||||
|
||||
chain = ventoy_alloc_chain(size);
|
||||
if (!chain)
|
||||
{
|
||||
grub_printf("Failed to alloc chain raw memory size %u\n", size);
|
||||
grub_file_close(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ventoy_memfile_env_set("vtoy_chain_mem", chain, (ulonglong)size);
|
||||
|
||||
grub_env_export("vtoy_chain_mem_addr");
|
||||
grub_env_export("vtoy_chain_mem_size");
|
||||
|
||||
grub_memset(chain, 0, sizeof(ventoy_chain_head));
|
||||
|
||||
/* part 1: os parameter */
|
||||
g_ventoy_chain_type = ventoy_chain_linux;
|
||||
ventoy_fill_os_param(file, &(chain->os_param));
|
||||
|
||||
/* part 2: chain head */
|
||||
disk = file->device->disk;
|
||||
chain->disk_drive = disk->id;
|
||||
chain->disk_sector_size = (1 << disk->log_sector_size);
|
||||
|
||||
chain->real_img_size_in_bytes = file->size;
|
||||
if (g_img_trim_head_secnum > 0)
|
||||
{
|
||||
chain->real_img_size_in_bytes -= g_img_trim_head_secnum * 512;
|
||||
}
|
||||
|
||||
chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes;
|
||||
chain->boot_catalog = 0;
|
||||
|
||||
/* part 3: image chunk */
|
||||
chain->img_chunk_offset = sizeof(ventoy_chain_head);
|
||||
chain->img_chunk_num = g_img_chunk_list.cur_chunk;
|
||||
grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
|
||||
|
||||
grub_file_seek(file, g_img_trim_head_secnum * 512);
|
||||
grub_file_read(file, chain->boot_catalog_sector, 512);
|
||||
|
||||
grub_file_close(file);
|
||||
|
||||
VENTOY_CMD_RETURN(GRUB_ERR_NONE);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
/******************************************************************************
|
||||
* wimboot.h
|
||||
*
|
||||
* Copyright (c) 2020, longpanda <[email protected]>
|
||||
*
|
||||
* 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; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef __WIMBOOT_H__
|
||||
#define __WIMBOOT_H__
|
||||
|
||||
#include <grub/types.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/device.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/net.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/crypto.h>
|
||||
#include <grub/ventoy.h>
|
||||
#include "ventoy_def.h"
|
||||
|
||||
|
||||
#define size_t grub_size_t
|
||||
#define ssize_t grub_ssize_t
|
||||
#define memset grub_memset
|
||||
#define memcpy grub_memcpy
|
||||
|
||||
#define uint8_t grub_uint8_t
|
||||
#define uint16_t grub_uint16_t
|
||||
#define uint32_t grub_uint32_t
|
||||
#define uint64_t grub_uint64_t
|
||||
#define int32_t grub_int32_t
|
||||
|
||||
|
||||
|
||||
#define assert(exp)
|
||||
|
||||
//#define DBG grub_printf
|
||||
#define DBG(fmt, ...)
|
||||
|
||||
const char * huffman_bin ( unsigned long value, unsigned int bits );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Xpress Compression Algorithm (MS-XCA) decompression
|
||||
*
|
||||
*/
|
||||
|
||||
#include "wimboot.h"
|
||||
#include "huffman.h"
|
||||
#include "xpress.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||
|
||||
/**
|
||||
* Decompress XCA-compressed data
|
||||
*
|
||||
* @v data Compressed data
|
||||
* @v len Length of compressed data
|
||||
* @v buf Decompression buffer, or NULL
|
||||
* @ret out_len Length of decompressed data, or negative error
|
||||
*/
|
||||
ssize_t xca_decompress ( const void *data, size_t len, void *buf ) {
|
||||
const void *src = data;
|
||||
const void *end = ( uint8_t * ) src + len;
|
||||
uint8_t *out = buf;
|
||||
size_t out_len = 0;
|
||||
size_t out_len_threshold = 0;
|
||||
const struct xca_huf_len *lengths;
|
||||
struct xca xca;
|
||||
uint32_t accum = 0;
|
||||
int extra_bits = 0;
|
||||
unsigned int huf;
|
||||
struct huffman_symbols *sym;
|
||||
unsigned int raw;
|
||||
unsigned int match_len;
|
||||
unsigned int match_offset_bits;
|
||||
unsigned int match_offset;
|
||||
const uint8_t *copy;
|
||||
int rc;
|
||||
|
||||
/* Process data stream */
|
||||
while ( src < end ) {
|
||||
|
||||
/* (Re)initialise decompressor if applicable */
|
||||
if ( out_len >= out_len_threshold ) {
|
||||
|
||||
/* Construct symbol lengths */
|
||||
lengths = src;
|
||||
src = ( uint8_t * ) src + sizeof ( *lengths );
|
||||
if ( src > end ) {
|
||||
DBG ( "XCA too short to hold Huffman lengths table.\n");
|
||||
return -1;
|
||||
}
|
||||
for ( raw = 0 ; raw < XCA_CODES ; raw++ )
|
||||
xca.lengths[raw] = xca_huf_len ( lengths, raw );
|
||||
|
||||
/* Construct Huffman alphabet */
|
||||
if ( ( rc = huffman_alphabet ( &xca.alphabet,
|
||||
xca.lengths,
|
||||
XCA_CODES ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Initialise state */
|
||||
accum = XCA_GET16 ( src );
|
||||
accum <<= 16;
|
||||
accum |= XCA_GET16 ( src );
|
||||
extra_bits = 16;
|
||||
|
||||
/* Determine next threshold */
|
||||
out_len_threshold = ( out_len + XCA_BLOCK_SIZE );
|
||||
}
|
||||
|
||||
/* Determine symbol */
|
||||
huf = ( accum >> ( 32 - HUFFMAN_BITS ) );
|
||||
sym = huffman_sym ( &xca.alphabet, huf );
|
||||
raw = huffman_raw ( sym, huf );
|
||||
accum <<= huffman_len ( sym );
|
||||
extra_bits -= huffman_len ( sym );
|
||||
if ( extra_bits < 0 ) {
|
||||
accum |= ( XCA_GET16 ( src ) << ( -extra_bits ) );
|
||||
extra_bits += 16;
|
||||
}
|
||||
|
||||
/* Process symbol */
|
||||
if ( raw < XCA_END_MARKER ) {
|
||||
|
||||
/* Literal symbol - add to output stream */
|
||||
if ( buf )
|
||||
*(out++) = raw;
|
||||
out_len++;
|
||||
|
||||
} else if ( ( raw == XCA_END_MARKER ) &&
|
||||
( (uint8_t *) src >= ( ( uint8_t * ) end - 1 ) ) ) {
|
||||
|
||||
/* End marker symbol */
|
||||
return out_len;
|
||||
|
||||
} else {
|
||||
|
||||
/* LZ77 match symbol */
|
||||
raw -= XCA_END_MARKER;
|
||||
match_offset_bits = ( raw >> 4 );
|
||||
match_len = ( raw & 0x0f );
|
||||
if ( match_len == 0x0f ) {
|
||||
match_len = XCA_GET8 ( src );
|
||||
if ( match_len == 0xff ) {
|
||||
match_len = XCA_GET16 ( src );
|
||||
} else {
|
||||
match_len += 0x0f;
|
||||
}
|
||||
}
|
||||
match_len += 3;
|
||||
if ( match_offset_bits ) {
|
||||
match_offset =
|
||||
( ( accum >> ( 32 - match_offset_bits ))
|
||||
+ ( 1 << match_offset_bits ) );
|
||||
} else {
|
||||
match_offset = 1;
|
||||
}
|
||||
accum <<= match_offset_bits;
|
||||
extra_bits -= match_offset_bits;
|
||||
if ( extra_bits < 0 ) {
|
||||
accum |= ( XCA_GET16 ( src ) << (-extra_bits) );
|
||||
extra_bits += 16;
|
||||
}
|
||||
|
||||
/* Copy data */
|
||||
out_len += match_len;
|
||||
if ( buf ) {
|
||||
copy = ( out - match_offset );
|
||||
while ( match_len-- )
|
||||
*(out++) = *(copy++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out_len;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef _XCA_H
|
||||
#define _XCA_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <[email protected]>.
|
||||
*
|
||||
* 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; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Xpress Compression Algorithm (MS-XCA) decompression
|
||||
*
|
||||
*/
|
||||
|
||||
#include "huffman.h"
|
||||
|
||||
/** Number of XCA codes */
|
||||
#define XCA_CODES 512
|
||||
|
||||
/** XCA decompressor */
|
||||
struct xca {
|
||||
/** Huffman alphabet */
|
||||
struct huffman_alphabet alphabet;
|
||||
/** Raw symbols
|
||||
*
|
||||
* Must immediately follow the Huffman alphabet.
|
||||
*/
|
||||
huffman_raw_symbol_t raw[XCA_CODES];
|
||||
/** Code lengths */
|
||||
uint8_t lengths[XCA_CODES];
|
||||
};
|
||||
|
||||
/** XCA symbol Huffman lengths table */
|
||||
struct xca_huf_len {
|
||||
/** Lengths of each symbol */
|
||||
uint8_t nibbles[ XCA_CODES / 2 ];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/**
|
||||
* Extract Huffman-coded length of a raw symbol
|
||||
*
|
||||
* @v lengths Huffman lengths table
|
||||
* @v symbol Raw symbol
|
||||
* @ret len Huffman-coded length
|
||||
*/
|
||||
static inline unsigned int xca_huf_len ( const struct xca_huf_len *lengths,
|
||||
unsigned int symbol ) {
|
||||
return ( ( ( lengths->nibbles[ symbol / 2 ] ) >>
|
||||
( 4 * ( symbol % 2 ) ) ) & 0x0f );
|
||||
}
|
||||
|
||||
/** Get word from source data stream */
|
||||
#define XCA_GET16( src ) ( { \
|
||||
const uint16_t *src16 = src; \
|
||||
src = ( uint8_t * ) src + sizeof ( *src16 ); \
|
||||
*src16; } )
|
||||
|
||||
/** Get byte from source data stream */
|
||||
#define XCA_GET8( src ) ( { \
|
||||
const uint8_t *src8 = src; \
|
||||
src = ( uint8_t * ) src + sizeof ( *src8 ); \
|
||||
*src8; } )
|
||||
|
||||
/** XCA source data stream end marker */
|
||||
#define XCA_END_MARKER 256
|
||||
|
||||
/** XCA block size */
|
||||
#define XCA_BLOCK_SIZE ( 64 * 1024 )
|
||||
|
||||
extern ssize_t xca_decompress ( const void *data, size_t len, void *buf );
|
||||
|
||||
#endif /* _XCA_H */
|
||||
Reference in New Issue
Block a user