Several AFFS fixes.

* grub-core/fs/affs.c (grub_affs_bblock): Replace flags with version.
	(GRUB_AFFS_FLAG_FFS): Removed.
	(GRUB_AFFS_SYMLINK_SIZE): Likewise.
	(GRUB_AFFS_FILETYPE_DIR): Make positive and unsigned.
	(GRUB_AFFS_FILETYPE_DIR), (GRUB_AFFS_FILETYPE_REG): Fix a mix-up.
	(grub_fshelp_node): Make block 32-bit.
	Add block_cache and last_block_cache.
	(grub_affs_read_block): Fill and use block cache.
	(grub_affs_read_file): Removed.
	(grub_affs_mount): Zero-fill node. Fix version check. Don't reread
	boot block.
	(grub_affs_read_symlink): Fix symlink size. Add a \0 at the end for
	safety.
	(grub_affs_iterate_dir): Use more appropriate types. Zero-fill allocated
	space.
	(grub_affs_close): Free block cache.
	(grub_affs_read): Use grub_fshelp_read_file directly.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-11-09 11:43:39 +01:00
parent 438a746a3f
commit 52b656c037
2 changed files with 74 additions and 59 deletions

View file

@ -1,3 +1,25 @@
2011-11-09 Vladimir Serbinenko <phcoder@gmail.com>
Several AFFS fixes.
* grub-core/fs/affs.c (grub_affs_bblock): Replace flags with version.
(GRUB_AFFS_FLAG_FFS): Removed.
(GRUB_AFFS_SYMLINK_SIZE): Likewise.
(GRUB_AFFS_FILETYPE_DIR): Make positive and unsigned.
(GRUB_AFFS_FILETYPE_DIR), (GRUB_AFFS_FILETYPE_REG): Fix a mix-up.
(grub_fshelp_node): Make block 32-bit.
Add block_cache and last_block_cache.
(grub_affs_read_block): Fill and use block cache.
(grub_affs_read_file): Removed.
(grub_affs_mount): Zero-fill node. Fix version check. Don't reread
boot block.
(grub_affs_read_symlink): Fix symlink size. Add a \0 at the end for
safety.
(grub_affs_iterate_dir): Use more appropriate types. Zero-fill allocated
space.
(grub_affs_close): Free block cache.
(grub_affs_read): Use grub_fshelp_read_file directly.
2011-11-08 Vladimir Serbinenko <phcoder@gmail.com> 2011-11-08 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/fs/zfs/zfs.c (read_dva): Issue an error if read failed * grub-core/fs/zfs/zfs.c (read_dva): Issue an error if read failed

View file

@ -32,15 +32,11 @@ GRUB_MOD_LICENSE ("GPLv3+");
struct grub_affs_bblock struct grub_affs_bblock
{ {
grub_uint8_t type[3]; grub_uint8_t type[3];
grub_uint8_t flags; grub_uint8_t version;
grub_uint32_t checksum; grub_uint32_t checksum;
grub_uint32_t rootblock; grub_uint32_t rootblock;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* Set if the filesystem is a AFFS filesystem. Otherwise this is an
OFS filesystem. */
#define GRUB_AFFS_FLAG_FFS 1
/* The affs rootblock. */ /* The affs rootblock. */
struct grub_affs_rblock struct grub_affs_rblock
{ {
@ -85,19 +81,19 @@ struct grub_affs_file
#define GRUB_AFFS_BLOCKPTR_OFFSET 24 #define GRUB_AFFS_BLOCKPTR_OFFSET 24
#define GRUB_AFFS_SYMLINK_OFFSET 24 #define GRUB_AFFS_SYMLINK_OFFSET 24
#define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225) #define GRUB_AFFS_FILETYPE_REG 0xfffffffd
#define GRUB_AFFS_FILETYPE_DIR 2
#define GRUB_AFFS_FILETYPE_DIR -3
#define GRUB_AFFS_FILETYPE_REG 2
#define GRUB_AFFS_FILETYPE_SYMLINK 3 #define GRUB_AFFS_FILETYPE_SYMLINK 3
struct grub_fshelp_node struct grub_fshelp_node
{ {
struct grub_affs_data *data; struct grub_affs_data *data;
grub_disk_addr_t block; grub_uint32_t block;
struct grub_fshelp_node *parent; struct grub_fshelp_node *parent;
struct grub_affs_file di; struct grub_affs_file di;
grub_uint32_t *block_cache;
grub_uint32_t last_block_cache;
}; };
/* Information about a "mounted" affs filesystem. */ /* Information about a "mounted" affs filesystem. */
@ -120,32 +116,46 @@ static grub_dl_t my_mod;
static grub_disk_addr_t static grub_disk_addr_t
grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
{ {
int links; grub_uint32_t target, curblock;
grub_uint32_t pos; grub_uint32_t pos;
int block = node->block;
struct grub_affs_file file; struct grub_affs_file file;
struct grub_affs_data *data = node->data; struct grub_affs_data *data = node->data;
grub_uint64_t mod; grub_uint64_t mod;
if (!node->block_cache)
{
node->block_cache = grub_malloc ((((grub_be_to_cpu32 (node->di.size)
+ 511) >> 9) / data->htsize + 1)
* sizeof (node->block_cache[0]));
if (!node->block_cache)
return -1;
node->last_block_cache = 0;
node->block_cache[0] = node->block;
}
/* Files are at most 2G on AFFS, so no need for 64-bit division. */
target = (grub_uint32_t) fileblock / data->htsize;
mod = (grub_uint32_t) fileblock % data->htsize;
/* Find the block that points to the fileblock we are looking up by /* Find the block that points to the fileblock we are looking up by
following the chain until the right table is reached. */ following the chain until the right table is reached. */
for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--) for (curblock = node->last_block_cache + 1; curblock <= target; curblock++)
{ {
grub_disk_read (data->disk, block + data->blocksize - 1, grub_disk_read (data->disk,
node->block_cache[curblock - 1] + data->blocksize - 1,
data->blocksize * (GRUB_DISK_SECTOR_SIZE data->blocksize * (GRUB_DISK_SECTOR_SIZE
- GRUB_AFFS_FILE_LOCATION), - GRUB_AFFS_FILE_LOCATION),
sizeof (file), &file); sizeof (file), &file);
if (grub_errno) if (grub_errno)
return 0; return 0;
block = grub_be_to_cpu32 (file.extension); node->block_cache[curblock] = grub_be_to_cpu32 (file.extension);
node->last_block_cache = curblock;
} }
/* Translate the fileblock to the block within the right table. */ /* Translate the fileblock to the block within the right table. */
fileblock = mod; grub_disk_read (data->disk, node->block_cache[target],
grub_disk_read (data->disk, block,
GRUB_AFFS_BLOCKPTR_OFFSET GRUB_AFFS_BLOCKPTR_OFFSET
+ (data->htsize - fileblock - 1) * sizeof (pos), + (data->htsize - mod - 1) * sizeof (pos),
sizeof (pos), &pos); sizeof (pos), &pos);
if (grub_errno) if (grub_errno)
return 0; return 0;
@ -153,21 +163,6 @@ grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
return grub_be_to_cpu32 (pos); return grub_be_to_cpu32 (pos);
} }
/* Read LEN bytes from the file described by DATA starting with byte
POS. Return the amount of read bytes in READ. */
static grub_ssize_t
grub_affs_read_file (grub_fshelp_node_t node,
void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
unsigned offset, unsigned length),
grub_off_t pos, grub_size_t len, char *buf)
{
return grub_fshelp_read_file (node->data->disk, node, read_hook,
pos, len, buf, grub_affs_read_block,
grub_be_to_cpu32 (node->di.size), 0);
}
static struct grub_affs_data * static struct grub_affs_data *
grub_affs_mount (grub_disk_t disk) grub_affs_mount (grub_disk_t disk)
{ {
@ -178,7 +173,7 @@ grub_affs_mount (grub_disk_t disk)
int checksum = 0; int checksum = 0;
int blocksize = 0; int blocksize = 0;
data = grub_malloc (sizeof (struct grub_affs_data)); data = grub_zalloc (sizeof (struct grub_affs_data));
if (!data) if (!data)
return 0; return 0;
@ -196,18 +191,12 @@ grub_affs_mount (grub_disk_t disk)
} }
/* Test if the filesystem is a OFS filesystem. */ /* Test if the filesystem is a OFS filesystem. */
if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS)) if (data->bblock.version < 1)
{ {
grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported"); grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported");
goto fail; goto fail;
} }
/* Read the bootblock. */
grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock),
&data->bblock);
if (grub_errno)
goto fail;
/* No sane person uses more than 8KB for a block. At least I hope /* No sane person uses more than 8KB for a block. At least I hope
for that person because in that case this won't work. */ for that person because in that case this won't work. */
rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16); rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16);
@ -270,18 +259,21 @@ grub_affs_read_symlink (grub_fshelp_node_t node)
{ {
struct grub_affs_data *data = node->data; struct grub_affs_data *data = node->data;
char *symlink; char *symlink;
const grub_size_t symlink_size = (data->blocksize * GRUB_DISK_SECTOR_SIZE
- 225);
symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize)); symlink = grub_malloc (symlink_size + 1);
if (!symlink) if (!symlink)
return 0; return 0;
grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET, grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET,
GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink); symlink_size, symlink);
if (grub_errno) if (grub_errno)
{ {
grub_free (symlink); grub_free (symlink);
return 0; return 0;
} }
symlink[symlink_size] = 1;
grub_dprintf ("affs", "Symlink: `%s'\n", symlink); grub_dprintf ("affs", "Symlink: `%s'\n", symlink);
return symlink; return symlink;
} }
@ -301,24 +293,24 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
grub_uint32_t *hashtable; grub_uint32_t *hashtable;
auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name,
grub_disk_addr_t block, grub_uint32_t block,
const struct grub_affs_file *fil); const struct grub_affs_file *fil);
int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int NESTED_FUNC_ATTR grub_affs_create_node (const char *name,
grub_disk_addr_t block, grub_uint32_t block,
const struct grub_affs_file *fil) const struct grub_affs_file *fil)
{ {
int type; int type;
node = grub_malloc (sizeof (*node)); node = grub_zalloc (sizeof (*node));
if (!node) if (!node)
{ {
grub_free (hashtable); grub_free (hashtable);
return 1; return 1;
} }
if ((int) grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_DIR) if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_REG)
type = GRUB_FSHELP_REG; type = GRUB_FSHELP_REG;
else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_REG) else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_DIR)
type = GRUB_FSHELP_DIR; type = GRUB_FSHELP_DIR;
else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_SYMLINK) else if (grub_be_to_cpu32 (fil->type) == GRUB_AFFS_FILETYPE_SYMLINK)
type = GRUB_FSHELP_SYMLINK; type = GRUB_FSHELP_SYMLINK;
@ -339,7 +331,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
} }
/* Create the directory entries for `.' and `..'. */ /* Create the directory entries for `.' and `..'. */
node = grub_malloc (sizeof (*node)); node = grub_zalloc (sizeof (*node));
if (!node) if (!node)
return 1; return 1;
@ -348,7 +340,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
return 1; return 1;
if (dir->parent) if (dir->parent)
{ {
node = grub_malloc (sizeof (*node)); node = grub_zalloc (sizeof (*node));
if (!node) if (!node)
return 1; return 1;
*node = *dir->parent; *node = *dir->parent;
@ -356,7 +348,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
return 1; return 1;
} }
hashtable = grub_malloc (data->htsize * sizeof (*hashtable)); hashtable = grub_zalloc (data->htsize * sizeof (*hashtable));
if (!hashtable) if (!hashtable)
return 1; return 1;
@ -367,7 +359,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
for (i = 0; i < data->htsize; i++) for (i = 0; i < data->htsize; i++)
{ {
grub_uint64_t next; grub_uint32_t next;
if (!hashtable[i]) if (!hashtable[i])
continue; continue;
@ -441,10 +433,13 @@ grub_affs_open (struct grub_file *file, const char *name)
return grub_errno; return grub_errno;
} }
static grub_err_t static grub_err_t
grub_affs_close (grub_file_t file) grub_affs_close (grub_file_t file)
{ {
struct grub_affs_data *data =
(struct grub_affs_data *) file->data;
grub_free (data->diropen.block_cache);
grub_free (file->data); grub_free (file->data);
grub_dl_unref (my_mod); grub_dl_unref (my_mod);
@ -452,7 +447,6 @@ grub_affs_close (grub_file_t file)
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
/* Read LEN bytes data from FILE into BUF. */ /* Read LEN bytes data from FILE into BUF. */
static grub_ssize_t static grub_ssize_t
grub_affs_read (grub_file_t file, char *buf, grub_size_t len) grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
@ -460,13 +454,12 @@ grub_affs_read (grub_file_t file, char *buf, grub_size_t len)
struct grub_affs_data *data = struct grub_affs_data *data =
(struct grub_affs_data *) file->data; (struct grub_affs_data *) file->data;
int size = grub_affs_read_file (&data->diropen, file->read_hook, return grub_fshelp_read_file (data->diropen.data->disk, &data->diropen,
file->offset, len, buf); file->read_hook,
file->offset, len, buf, grub_affs_read_block,
return size; grub_be_to_cpu32 (data->diropen.di.size), 0);
} }
static grub_err_t static grub_err_t
grub_affs_dir (grub_device_t device, const char *path, grub_affs_dir (grub_device_t device, const char *path,
int (*hook) (const char *filename, int (*hook) (const char *filename,