#include <stddef.h>
#include <linux/stat.h>
#include <linux/time.h>
-#include <linux/list.h>
#include <asm/byteorder.h>
#include "ext4_common.h"
struct ext2_inode *g_parent_inode;
static int symlinknest;
-struct ext4_extent_node {
- uint32_t block;
- uint16_t len;
- uint64_t start;
- struct list_head lh;
-};
-static LIST_HEAD(ext4_extent_lh);
-
#if defined(CONFIG_EXT4_WRITE)
uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n)
{
#endif
-static void ext4fs_extent_cache_insert(struct ext4_extent_node *new)
-{
- struct ext4_extent_node *node;
-
- list_for_each_entry(node, &ext4_extent_lh, lh)
- if (node->block > new->block) {
- list_add_tail(&new->lh, &node->lh);
- return;
- }
- list_add_tail(&new->lh, &ext4_extent_lh);
-}
-
-static int __ext4fs_build_extent_cache(struct ext2_data *data,
- struct ext4_extent_header *ext_block)
+static struct ext4_extent_header *ext4fs_get_extent_block
+ (struct ext2_data *data, char *buf,
+ struct ext4_extent_header *ext_block,
+ uint32_t fileblock, int log2_blksz)
{
- int blksz = EXT2_BLOCK_SIZE(data);
- int log2_blksz = LOG2_BLOCK_SIZE(data)
- - get_fs()->dev_desc->log2blksz;
- struct ext4_extent_node *node;
struct ext4_extent_idx *index;
- struct ext4_extent *extent;
unsigned long long block;
- char *buf;
- int i, err;
+ int blksz = EXT2_BLOCK_SIZE(data);
+ int i;
- if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
- return -EINVAL;
+ while (1) {
+ index = (struct ext4_extent_idx *)(ext_block + 1);
- if (ext_block->eh_depth == 0) {
- extent = (struct ext4_extent *)(ext_block + 1);
- for (i = 0; i < le16_to_cpu(ext_block->eh_entries); i++) {
- node = malloc(sizeof(*node));
- if (!node)
- return -ENOMEM;
- node->block = le32_to_cpu(extent[i].ee_block);
- node->len = le16_to_cpu(extent[i].ee_len);
- node->start = le16_to_cpu(extent[i].ee_start_hi);
- node->start = (node->start << 32) +
- le32_to_cpu(extent[i].ee_start_lo);
- ext4fs_extent_cache_insert(node);
- }
- return 0;
- }
+ if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
+ return 0;
- index = (struct ext4_extent_idx *)(ext_block + 1);
- for (i = 0; i < le16_to_cpu(ext_block->eh_entries); i++) {
- buf = malloc(blksz);
- if (!buf)
- return -ENOMEM;
+ if (ext_block->eh_depth == 0)
+ return ext_block;
+ i = -1;
+ do {
+ i++;
+ if (i >= le16_to_cpu(ext_block->eh_entries))
+ break;
+ } while (fileblock >= le32_to_cpu(index[i].ei_block));
+
+ if (--i < 0)
+ return 0;
block = le16_to_cpu(index[i].ei_leaf_hi);
block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo);
- if (!ext4fs_devread(block << log2_blksz, 0, blksz, buf)) {
- free(buf);
- return -EIO;
- }
-
- err = __ext4fs_build_extent_cache(data,
- (struct ext4_extent_header *) buf);
- free(buf);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-int ext4fs_build_extent_cache(struct ext2_inode *inode)
-{
- return __ext4fs_build_extent_cache(ext4fs_root,
- (struct ext4_extent_header *)
- inode->b.blocks.dir_blocks);
-}
-
-void ext4fs_free_extent_cache(void)
-{
- struct ext4_extent_node *node, *tmp;
-
- list_for_each_entry_safe(node, tmp, &ext4_extent_lh, lh) {
- list_del(&node->lh);
- free(node);
+ if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz,
+ buf))
+ ext_block = (struct ext4_extent_header *)buf;
+ else
+ return 0;
}
}
-static struct ext4_extent_node *ext4fs_extent_cache_get(uint32_t block)
-{
- struct ext4_extent_node *node;
-
- list_for_each_entry(node, &ext4_extent_lh, lh)
- if (block >= node->block && block < node->block + node->len)
- return node;
-
- return NULL;
-}
-
static int ext4fs_blockgroup
(struct ext2_data *data, int group, struct ext2_block_group *blkgrp)
{
long int rblock;
long int perblock_parent;
long int perblock_child;
-
+ unsigned long long start;
/* get the blocksize of the filesystem */
blksz = EXT2_BLOCK_SIZE(ext4fs_root);
log2_blksz = LOG2_BLOCK_SIZE(ext4fs_root)
- get_fs()->dev_desc->log2blksz;
if (le32_to_cpu(inode->flags) & EXT4_EXTENTS_FL) {
- struct ext4_extent_node *node;
+ char *buf = zalloc(blksz);
+ if (!buf)
+ return -ENOMEM;
+ struct ext4_extent_header *ext_block;
+ struct ext4_extent *extent;
+ int i = -1;
+ ext_block =
+ ext4fs_get_extent_block(ext4fs_root, buf,
+ (struct ext4_extent_header *)
+ inode->b.blocks.dir_blocks,
+ fileblock, log2_blksz);
+ if (!ext_block) {
+ printf("invalid extent block\n");
+ free(buf);
+ return -EINVAL;
+ }
- node = ext4fs_extent_cache_get(fileblock);
- if (!node) {
- printf("Extent Error\n");
- return -1;
+ extent = (struct ext4_extent *)(ext_block + 1);
+
+ do {
+ i++;
+ if (i >= le16_to_cpu(ext_block->eh_entries))
+ break;
+ } while (fileblock >= le32_to_cpu(extent[i].ee_block));
+ if (--i >= 0) {
+ fileblock -= le32_to_cpu(extent[i].ee_block);
+ if (fileblock >= le16_to_cpu(extent[i].ee_len)) {
+ free(buf);
+ return 0;
+ }
+
+ start = le16_to_cpu(extent[i].ee_start_hi);
+ start = (start << 32) +
+ le32_to_cpu(extent[i].ee_start_lo);
+ free(buf);
+ return fileblock + start;
}
- return fileblock - node->block + node->start;
+ printf("Extent Error\n");
+ free(buf);
+ return -1;
}
/* Direct blocks. */