* grub-core/fs/sfs.c (grub_sfs_rblock): New fields createtime and
flags. (FLAGS_CASE_SENSITIVE): New enum value. (cache_entry): New struct. (grub_fshelp_node): Add fields cache_off, next_extent, cache_allocated, cache_size and cache. (grub_sfs_data): Remove blocksize. All users switched to log_blocksize. Add log_blocksize and fshelp_flags. (grub_sfs_read_extent): Handle non-512 blocks. (grub_sfs_read_block): Add cаche and handle non-512 blocks. (grub_sfs_read_file): Handle non-512 blocks. (grub_sfs_mount): Handle non-512 blocks. Fill log_blocksize and fshelp_flags. (grub_sfs_read_symlink): Handle non-512 blocks. (grub_sfs_iterate_dir): Init new fields. Mark as case-insensitive. (grub_sfs_dir): Free cache. (grub_sfs_close): Likewise.
This commit is contained in:
parent
24e9a854b8
commit
dacd85cfb0
2 changed files with 172 additions and 31 deletions
20
ChangeLog
20
ChangeLog
|
@ -1,3 +1,23 @@
|
|||
2012-05-07 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
* grub-core/fs/sfs.c (grub_sfs_rblock): New fields createtime and
|
||||
flags.
|
||||
(FLAGS_CASE_SENSITIVE): New enum value.
|
||||
(cache_entry): New struct.
|
||||
(grub_fshelp_node): Add fields cache_off, next_extent, cache_allocated,
|
||||
cache_size and cache.
|
||||
(grub_sfs_data): Remove blocksize. All users switched to log_blocksize.
|
||||
Add log_blocksize and fshelp_flags.
|
||||
(grub_sfs_read_extent): Handle non-512 blocks.
|
||||
(grub_sfs_read_block): Add cаche and handle non-512 blocks.
|
||||
(grub_sfs_read_file): Handle non-512 blocks.
|
||||
(grub_sfs_mount): Handle non-512 blocks. Fill log_blocksize and
|
||||
fshelp_flags.
|
||||
(grub_sfs_read_symlink): Handle non-512 blocks.
|
||||
(grub_sfs_iterate_dir): Init new fields. Mark as case-insensitive.
|
||||
(grub_sfs_dir): Free cache.
|
||||
(grub_sfs_close): Likewise.
|
||||
|
||||
2012-05-06 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
* grub-core/fs/bfs.c (read_bfs_file): Fix overflow with over 2TiB
|
||||
|
|
|
@ -42,7 +42,9 @@ struct grub_sfs_rblock
|
|||
{
|
||||
struct grub_sfs_bheader header;
|
||||
grub_uint32_t version;
|
||||
grub_uint8_t unused1[36];
|
||||
grub_uint32_t createtime;
|
||||
grub_uint8_t flags;
|
||||
grub_uint8_t unused1[31];
|
||||
grub_uint32_t blocksize;
|
||||
grub_uint8_t unused2[40];
|
||||
grub_uint8_t unused3[8];
|
||||
|
@ -50,6 +52,11 @@ struct grub_sfs_rblock
|
|||
grub_uint32_t btree;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum
|
||||
{
|
||||
FLAGS_CASE_SENSITIVE = 0x80
|
||||
};
|
||||
|
||||
/* A SFS object container. */
|
||||
struct grub_sfs_obj
|
||||
{
|
||||
|
@ -117,12 +124,23 @@ struct grub_sfs_btree
|
|||
|
||||
|
||||
|
||||
struct cache_entry
|
||||
{
|
||||
grub_uint32_t off;
|
||||
grub_uint32_t block;
|
||||
};
|
||||
|
||||
struct grub_fshelp_node
|
||||
{
|
||||
struct grub_sfs_data *data;
|
||||
grub_uint32_t block;
|
||||
grub_uint32_t size;
|
||||
grub_uint32_t mtime;
|
||||
grub_uint32_t cache_off;
|
||||
grub_uint32_t next_extent;
|
||||
grub_size_t cache_allocated;
|
||||
grub_size_t cache_size;
|
||||
struct cache_entry *cache;
|
||||
};
|
||||
|
||||
/* Information about a "mounted" sfs filesystem. */
|
||||
|
@ -132,8 +150,10 @@ struct grub_sfs_data
|
|||
struct grub_fshelp_node diropen;
|
||||
grub_disk_t disk;
|
||||
|
||||
/* Blocksize in sectors. */
|
||||
unsigned int blocksize;
|
||||
/* Log of blocksize in sectors. */
|
||||
int log_blocksize;
|
||||
|
||||
int fshelp_flags;
|
||||
|
||||
/* Label of the filesystem. */
|
||||
char *label;
|
||||
|
@ -154,7 +174,7 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
|
|||
int i;
|
||||
grub_uint32_t next;
|
||||
|
||||
treeblock = grub_malloc (data->blocksize);
|
||||
treeblock = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
|
@ -164,7 +184,10 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
|
|||
/* Handle this level in the btree. */
|
||||
do
|
||||
{
|
||||
grub_disk_read (data->disk, next, 0, data->blocksize, treeblock);
|
||||
grub_disk_read (data->disk,
|
||||
((grub_disk_addr_t) next) << data->log_blocksize,
|
||||
0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize,
|
||||
treeblock);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (treeblock);
|
||||
|
@ -213,27 +236,100 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
|
|||
static grub_disk_addr_t
|
||||
grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
|
||||
{
|
||||
grub_uint32_t blk = node->block;
|
||||
grub_uint32_t blk;
|
||||
grub_uint32_t size = 0;
|
||||
grub_uint32_t next = 0;
|
||||
grub_disk_addr_t off;
|
||||
struct grub_sfs_data *data = node->data;
|
||||
|
||||
/* In case of the first block we don't have to lookup the
|
||||
extent, the minimum size is always 1. */
|
||||
if (fileblock == 0)
|
||||
return node->block;
|
||||
|
||||
if (!node->cache)
|
||||
{
|
||||
grub_size_t cache_size;
|
||||
/* Assume half-max extents (32768 sectors). */
|
||||
cache_size = ((node->size >> (data->log_blocksize + GRUB_DISK_SECTOR_BITS
|
||||
+ 15))
|
||||
+ 3);
|
||||
if (cache_size < 8)
|
||||
cache_size = 8;
|
||||
|
||||
node->cache_off = 0;
|
||||
node->next_extent = node->block;
|
||||
node->cache_size = 0;
|
||||
|
||||
node->cache = grub_malloc (sizeof (node->cache[0]) * cache_size);
|
||||
if (!node->cache)
|
||||
{
|
||||
grub_errno = 0;
|
||||
node->cache_allocated = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->cache_allocated = cache_size;
|
||||
node->cache[0].off = 0;
|
||||
node->cache[0].block = node->block;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileblock < node->cache_off)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
int j, lg;
|
||||
for (lg = 0; node->cache_size >> lg; lg++);
|
||||
|
||||
for (j = lg - 1; j >= 0; j--)
|
||||
if ((i | (1 << j)) < node->cache_size
|
||||
&& node->cache[(i | (1 << j))].off <= fileblock)
|
||||
i |= (1 << j);
|
||||
return node->cache[i].block + fileblock - node->cache[i].off;
|
||||
}
|
||||
|
||||
off = node->cache_off;
|
||||
blk = node->next_extent;
|
||||
|
||||
while (blk)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
/* In case of the first block we don't have to lookup the
|
||||
extent, the minimum size is always 1. */
|
||||
if (fileblock == 0)
|
||||
return blk;
|
||||
|
||||
err = grub_sfs_read_extent (node->data, blk, &size, &next);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
if (fileblock < size)
|
||||
return fileblock + blk;
|
||||
if (node->cache && node->cache_size >= node->cache_allocated)
|
||||
{
|
||||
struct cache_entry *e = node->cache;
|
||||
e = grub_realloc (node->cache,node->cache_allocated * 2
|
||||
* sizeof (e[0]));
|
||||
if (!e)
|
||||
{
|
||||
grub_errno = 0;
|
||||
grub_free (node->cache);
|
||||
node->cache = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->cache_allocated *= 2;
|
||||
node->cache = e;
|
||||
}
|
||||
}
|
||||
|
||||
fileblock -= size;
|
||||
if (node->cache)
|
||||
{
|
||||
node->cache_off = off + size;
|
||||
node->next_extent = next;
|
||||
node->cache[node->cache_size].off = off;
|
||||
node->cache[node->cache_size].block = blk;
|
||||
node->cache_size++;
|
||||
}
|
||||
|
||||
if (fileblock - off < size)
|
||||
return fileblock - off + blk;
|
||||
|
||||
off += size;
|
||||
|
||||
blk = next;
|
||||
}
|
||||
|
@ -255,7 +351,7 @@ grub_sfs_read_file (grub_fshelp_node_t node,
|
|||
{
|
||||
return grub_fshelp_read_file (node->data->disk, node, read_hook,
|
||||
pos, len, buf, grub_sfs_read_block,
|
||||
node->size, 0);
|
||||
node->size, node->data->log_blocksize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -278,20 +374,31 @@ grub_sfs_mount (grub_disk_t disk)
|
|||
goto fail;
|
||||
|
||||
/* Make sure this is a sfs filesystem. */
|
||||
if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4))
|
||||
if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4)
|
||||
|| data->rblock.blocksize == 0
|
||||
|| (data->rblock.blocksize & (data->rblock.blocksize - 1)) != 0
|
||||
|| (data->rblock.blocksize & grub_cpu_to_be32_compile_time (0xf00001ff)))
|
||||
{
|
||||
grub_error (GRUB_ERR_BAD_FS, "not a SFS filesystem");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data->blocksize = grub_be_to_cpu32 (data->rblock.blocksize);
|
||||
rootobjc_data = grub_malloc (data->blocksize);
|
||||
for (data->log_blocksize = 9;
|
||||
(1U << data->log_blocksize) < grub_be_to_cpu32 (data->rblock.blocksize);
|
||||
data->log_blocksize++);
|
||||
data->log_blocksize -= GRUB_DISK_SECTOR_BITS;
|
||||
if (data->rblock.flags & FLAGS_CASE_SENSITIVE)
|
||||
data->fshelp_flags = 0;
|
||||
else
|
||||
data->fshelp_flags = GRUB_FSHELP_CASE_INSENSITIVE;
|
||||
rootobjc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
|
||||
if (! rootobjc_data)
|
||||
goto fail;
|
||||
|
||||
/* Read the root object container. */
|
||||
grub_disk_read (disk, grub_be_to_cpu32 (data->rblock.rootobject), 0,
|
||||
data->blocksize, rootobjc_data);
|
||||
grub_disk_read (disk, ((grub_disk_addr_t) grub_be_to_cpu32 (data->rblock.rootobject))
|
||||
<< data->log_blocksize, 0,
|
||||
GRUB_DISK_SECTOR_SIZE << data->log_blocksize, rootobjc_data);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
|
@ -301,6 +408,7 @@ grub_sfs_mount (grub_disk_t disk)
|
|||
data->diropen.size = 0;
|
||||
data->diropen.block = blk;
|
||||
data->diropen.data = data;
|
||||
data->diropen.cache = 0;
|
||||
data->disk = disk;
|
||||
data->label = grub_strdup ((char *) (rootobjc->objects[0].filename));
|
||||
|
||||
|
@ -324,11 +432,13 @@ grub_sfs_read_symlink (grub_fshelp_node_t node)
|
|||
char *symlink;
|
||||
char *block;
|
||||
|
||||
block = grub_malloc (data->blocksize);
|
||||
block = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
grub_disk_read (data->disk, node->block, 0, data->blocksize, block);
|
||||
grub_disk_read (data->disk, ((grub_disk_addr_t) node->block)
|
||||
<< data->log_blocksize,
|
||||
0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize, block);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_free (block);
|
||||
|
@ -337,7 +447,8 @@ grub_sfs_read_symlink (grub_fshelp_node_t node)
|
|||
|
||||
/* This is just a wild guess, but it always worked for me. How the
|
||||
SLNK block looks like is not documented in the SFS docs. */
|
||||
symlink = grub_strndup (&block[24], data->blocksize - 24);
|
||||
symlink = grub_strndup (&block[24],
|
||||
(GRUB_DISK_SECTOR_SIZE << data->log_blocksize) - 24);
|
||||
grub_free (block);
|
||||
if (!symlink)
|
||||
return 0;
|
||||
|
@ -360,13 +471,13 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
|
|||
grub_uint32_t pos;
|
||||
|
||||
auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
|
||||
int block,
|
||||
int size, int type,
|
||||
grub_uint32_t block,
|
||||
grub_uint32_t size, int type,
|
||||
grub_uint32_t mtime);
|
||||
|
||||
int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
|
||||
int block,
|
||||
int size, int type,
|
||||
grub_uint32_t block,
|
||||
grub_uint32_t size, int type,
|
||||
grub_uint32_t mtime)
|
||||
{
|
||||
grub_size_t len = grub_strlen (name);
|
||||
|
@ -386,15 +497,20 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
|
|||
node->size = size;
|
||||
node->block = block;
|
||||
node->mtime = mtime;
|
||||
node->cache = 0;
|
||||
node->cache_off = 0;
|
||||
node->next_extent = block;
|
||||
node->cache_size = 0;
|
||||
node->cache_allocated = 0;
|
||||
|
||||
*grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0';
|
||||
|
||||
ret = hook ((char *) name_u8, type, node);
|
||||
ret = hook ((char *) name_u8, type | data->fshelp_flags, node);
|
||||
grub_free (name_u8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
objc_data = grub_malloc (data->blocksize);
|
||||
objc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
|
||||
if (!objc_data)
|
||||
goto fail;
|
||||
|
||||
|
@ -402,7 +518,9 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
|
|||
every block. */
|
||||
while (next)
|
||||
{
|
||||
grub_disk_read (data->disk, next, 0, data->blocksize, objc_data);
|
||||
grub_disk_read (data->disk, ((grub_disk_addr_t) next)
|
||||
<< data->log_blocksize, 0,
|
||||
GRUB_DISK_SECTOR_SIZE << data->log_blocksize, objc_data);
|
||||
if (grub_errno)
|
||||
goto fail;
|
||||
|
||||
|
@ -411,7 +529,8 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
|
|||
pos = (char *) &objc->objects[0] - (char *) objc;
|
||||
|
||||
/* Iterate over all entries in this block. */
|
||||
while (pos + sizeof (struct grub_sfs_obj) < data->blocksize)
|
||||
while (pos + sizeof (struct grub_sfs_obj)
|
||||
< (1U << (GRUB_DISK_SECTOR_BITS + data->log_blocksize)))
|
||||
{
|
||||
struct grub_sfs_obj *obj;
|
||||
obj = (struct grub_sfs_obj *) ((char *) objc + pos);
|
||||
|
@ -510,6 +629,7 @@ grub_sfs_close (grub_file_t file)
|
|||
{
|
||||
struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
|
||||
|
||||
grub_free (data->diropen.cache);
|
||||
grub_free (data->label);
|
||||
grub_free (data);
|
||||
|
||||
|
@ -551,6 +671,7 @@ grub_sfs_dir (grub_device_t device, const char *path,
|
|||
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
|
||||
info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
|
||||
info.mtimeset = 1;
|
||||
grub_free (node->cache);
|
||||
grub_free (node);
|
||||
return hook (filename, &info);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue