BtrFS support. Written by me (Vladimir) with important bugfixes and

even more important testing by Colin.

	* Makefile.util.def (libgrubmods.a): Add crc.c and gzio.c
	* grub-core/Makefile.core.def (btrfs): Add crc.c.
	* grub-core/fs/btrfs.c: Stub replaced with real implementation.
	* grub-core/io/gzio.c (grub_gzio): New fields mem_input_size,
	mem_input_off and mem_input. All users updated to accept in-RAM input.
	(gzio_seek): New function.
	(test_zlib_header): Likewise.
	(grub_gzio_read): Likewise.
	(grub_zlib_decompress): Likewise.
	* grub-core/kern/emu/getroot.c (grub_find_root_device_from_mountinfo):
	Accept partial and non-virtual mounts.
	(grub_guess_root_device): Do rescanning after device_from_mountinfo to
	avoid receiving /dev/dm-X as device.
	* grub-core/kern/emu/misc.c (grub_make_system_path_relative_to_its_root):
	Handle bind and partial mounts.
	* grub-core/lib/crc.c: New file.
	* include/grub/deflate.h: Likewise.
	* include/grub/emu/misc.h (grub_find_root_device_from_mountinfo): New
	proto.
	* include/grub/lib/crc.h: New file.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-04-11 23:26:41 +02:00
commit fe6b2cbaa6
11 changed files with 1711 additions and 100 deletions

View file

@ -41,6 +41,7 @@
#include <grub/fs.h>
#include <grub/file.h>
#include <grub/dl.h>
#include <grub/deflate.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -60,6 +61,9 @@ struct grub_gzio
{
/* The underlying file object. */
grub_file_t file;
/* If input is in memory following fields are used instead of file. */
grub_size_t mem_input_size, mem_input_off;
grub_uint8_t *mem_input;
/* The offset at which the data starts in the underlying file. */
grub_off_t data_offset;
/* The type of current block. */
@ -102,7 +106,7 @@ typedef struct grub_gzio *grub_gzio_t;
static struct grub_fs grub_gzio_fs;
/* Function prototypes */
static void initialize_tables (grub_file_t file);
static void initialize_tables (grub_gzio_t);
/* Eat variable-length header fields. */
static int
@ -164,7 +168,7 @@ typedef unsigned short ush;
typedef unsigned long ulg;
static int
test_header (grub_file_t file)
test_gzip_header (grub_file_t file)
{
struct {
grub_uint16_t magic;
@ -227,7 +231,7 @@ test_header (grub_file_t file)
file->size = grub_le_to_cpu32 (orig_len);
}
initialize_tables (file);
initialize_tables (gzio);
return 1;
}
@ -367,13 +371,18 @@ static ush mask_bits[] =
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(file))<<k;k+=8;}} while (0)
#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(gzio))<<k;k+=8;}} while (0)
#define DUMPBITS(n) do {b>>=(n);k-=(n);} while (0)
static int
get_byte (grub_file_t file)
get_byte (grub_gzio_t gzio)
{
grub_gzio_t gzio = file->data;
if (gzio->mem_input)
{
if (gzio->mem_input_off < gzio->mem_input_size)
return gzio->mem_input[gzio->mem_input_off++];
return 0;
}
if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset
|| gzio->inbuf_d == INBUFSIZ)
@ -385,11 +394,26 @@ get_byte (grub_file_t file)
return gzio->inbuf[gzio->inbuf_d++];
}
static void
gzio_seek (grub_gzio_t gzio, grub_off_t off)
{
if (gzio->mem_input)
{
if (off > gzio->mem_input_size)
grub_error (GRUB_ERR_OUT_OF_RANGE,
"attempt to seek outside of the file");
else
gzio->mem_input_off = gzio->data_offset;
}
else
grub_file_seek (gzio->file, off);
}
/* more function prototypes */
static int huft_build (unsigned *, unsigned, unsigned, ush *, ush *,
struct huft **, int *);
static int huft_free (struct huft *);
static int inflate_codes_in_window (grub_file_t);
static int inflate_codes_in_window (grub_gzio_t);
/* Given a list of code lengths and a maximum table size, make a set of
@ -616,7 +640,7 @@ huft_free (struct huft *t)
*/
static int
inflate_codes_in_window (grub_file_t file)
inflate_codes_in_window (grub_gzio_t gzio)
{
register unsigned e; /* table entry flag/number of extra bits */
unsigned n, d; /* length and index for copy */
@ -625,7 +649,6 @@ inflate_codes_in_window (grub_file_t file)
unsigned ml, md; /* masks for bl and bd bits */
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
grub_gzio_t gzio = file->data;
/* make local copies of globals */
d = gzio->inflate_d;
@ -753,11 +776,10 @@ inflate_codes_in_window (grub_file_t file)
/* get header for an inflated type 0 (stored) block. */
static void
init_stored_block (grub_file_t file)
init_stored_block (grub_gzio_t gzio)
{
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
grub_gzio_t gzio = file->data;
/* make local copies of globals */
b = gzio->bb; /* initialize bit buffer */
@ -787,11 +809,10 @@ init_stored_block (grub_file_t file)
Huffman tables. */
static void
init_fixed_block (grub_file_t file)
init_fixed_block (grub_gzio_t gzio)
{
int i; /* temporary variable */
unsigned l[288]; /* length list for huft_build */
grub_gzio_t gzio = file->data;
/* set up literal table */
for (i = 0; i < 144; i++)
@ -834,7 +855,7 @@ init_fixed_block (grub_file_t file)
/* get header for an inflated type 2 (dynamic Huffman codes) block. */
static void
init_dynamic_block (grub_file_t file)
init_dynamic_block (grub_gzio_t gzio)
{
int i; /* temporary variables */
unsigned j;
@ -847,7 +868,6 @@ init_dynamic_block (grub_file_t file)
unsigned ll[286 + 30]; /* literal/length and distance code lengths */
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
grub_gzio_t gzio = file->data;
/* make local bit buffer */
b = gzio->bb;
@ -978,11 +998,10 @@ init_dynamic_block (grub_file_t file)
static void
get_new_block (grub_file_t file)
get_new_block (grub_gzio_t gzio)
{
register ulg b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
grub_gzio_t gzio = file->data;
/* make local bit buffer */
b = gzio->bb;
@ -1005,13 +1024,13 @@ get_new_block (grub_file_t file)
switch (gzio->block_type)
{
case INFLATE_STORED:
init_stored_block (file);
init_stored_block (gzio);
break;
case INFLATE_FIXED:
init_fixed_block (file);
init_fixed_block (gzio);
break;
case INFLATE_DYNAMIC:
init_dynamic_block (file);
init_dynamic_block (gzio);
break;
default:
break;
@ -1020,10 +1039,8 @@ get_new_block (grub_file_t file)
static void
inflate_window (grub_file_t file)
inflate_window (grub_gzio_t gzio)
{
grub_gzio_t gzio = file->data;
/* initialize window */
gzio->wp = 0;
@ -1038,7 +1055,7 @@ inflate_window (grub_file_t file)
if (gzio->last_block)
break;
get_new_block (file);
get_new_block (gzio);
}
if (gzio->block_type > INFLATE_DYNAMIC)
@ -1061,7 +1078,7 @@ inflate_window (grub_file_t file)
while (gzio->block_len && w < WSIZE && grub_errno == GRUB_ERR_NONE)
{
gzio->slide[w++] = get_byte (file);
gzio->slide[w++] = get_byte (gzio);
gzio->block_len--;
}
@ -1074,7 +1091,7 @@ inflate_window (grub_file_t file)
* Expand other kind of block.
*/
if (inflate_codes_in_window (file))
if (inflate_codes_in_window (gzio))
{
huft_free (gzio->tl);
huft_free (gzio->td);
@ -1090,12 +1107,10 @@ inflate_window (grub_file_t file)
static void
initialize_tables (grub_file_t file)
initialize_tables (grub_gzio_t gzio)
{
grub_gzio_t gzio = file->data;
gzio->saved_offset = 0;
grub_file_seek (gzio->file, gzio->data_offset);
gzio_seek (gzio, gzio->data_offset);
/* Initialize the bit buffer. */
gzio->bk = 0;
@ -1140,7 +1155,7 @@ grub_gzio_open (grub_file_t io)
file->fs = &grub_gzio_fs;
file->not_easily_seekable = 1;
if (! test_header (file))
if (! test_gzip_header (file))
{
grub_free (gzio);
grub_free (file);
@ -1156,16 +1171,49 @@ grub_gzio_open (grub_file_t io)
return file;
}
static int
test_zlib_header (grub_gzio_t gzio)
{
grub_uint8_t cmf, flg;
cmf = get_byte (gzio);
flg = get_byte (gzio);
/* Check that compression method is DEFLATE. */
if ((cmf & 0xf) != DEFLATED)
{
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
return 0;
}
if ((cmf * 256 + flg) % 31)
{
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
return 0;
}
/* Dictionary isn't supported. */
if (flg & 0x20)
{
grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unsupported gzip format");
return 0;
}
gzio->data_offset = 2;
initialize_tables (gzio);
return 1;
}
static grub_ssize_t
grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
grub_gzio_read_real (grub_gzio_t gzio, grub_off_t offset,
char *buf, grub_size_t len)
{
grub_ssize_t ret = 0;
grub_gzio_t gzio = file->data;
grub_off_t offset;
/* Do we reset decompression to the beginning of the file? */
if (gzio->saved_offset > file->offset + WSIZE)
initialize_tables (file);
if (gzio->saved_offset > offset + WSIZE)
initialize_tables (gzio);
/*
* This loop operates upon uncompressed data only. The only
@ -1173,15 +1221,13 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
* window is within the range of data it needs.
*/
offset = file->offset;
while (len > 0 && grub_errno == GRUB_ERR_NONE)
{
register grub_size_t size;
register char *srcaddr;
while (offset >= gzio->saved_offset)
inflate_window (file);
inflate_window (gzio);
srcaddr = (char *) ((offset & (WSIZE - 1)) + gzio->slide);
size = gzio->saved_offset - offset;
@ -1202,6 +1248,12 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
return ret;
}
static grub_ssize_t
grub_gzio_read (grub_file_t file, char *buf, grub_size_t len)
{
return grub_gzio_read_real (file->data, file->offset, buf, len);
}
/* Release everything, including the underlying file object. */
static grub_err_t
grub_gzio_close (grub_file_t file)
@ -1219,6 +1271,33 @@ grub_gzio_close (grub_file_t file)
return grub_errno;
}
grub_ssize_t
grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off,
char *outbuf, grub_size_t outsize)
{
grub_gzio_t gzio = 0;
grub_ssize_t ret;
gzio = grub_zalloc (sizeof (*gzio));
if (! gzio)
return -1;
gzio->mem_input = (grub_uint8_t *) inbuf;
gzio->mem_input_size = insize;
gzio->mem_input_off = 0;
if (!test_zlib_header (gzio))
{
grub_free (gzio);
return -1;
}
ret = grub_gzio_read_real (gzio, off, outbuf, outsize);
grub_free (gzio);
/* FIXME: Check Adler. */
return ret;
}
static struct grub_fs grub_gzio_fs =