summaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/ext4_common.c164
-rw-r--r--fs/ext4/ext4_common.h3
-rw-r--r--fs/ext4/ext4fs.c36
3 files changed, 130 insertions, 73 deletions
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c
index 02da75c084..6584892dd2 100644
--- a/fs/ext4/ext4_common.c
+++ b/fs/ext4/ext4_common.c
@@ -26,6 +26,7 @@
#include <stddef.h>
#include <linux/stat.h>
#include <linux/time.h>
+#include <linux/list.h>
#include <asm/byteorder.h>
#include "ext4_common.h"
@@ -44,6 +45,14 @@ int ext4fs_indir3_blkno = -1;
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)
{
@@ -1407,45 +1416,102 @@ void ext4fs_allocate_blocks(struct ext2_inode *file_inode,
#endif
-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)
+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)
{
+ 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;
- int blksz = EXT2_BLOCK_SIZE(data);
- int i;
+ char *buf;
+ int i, err;
- while (1) {
- index = (struct ext4_extent_idx *)(ext_block + 1);
+ if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
+ return -EINVAL;
- if (le16_to_cpu(ext_block->eh_magic) != EXT4_EXT_MAGIC)
- return 0;
-
- 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 (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 (--i < 0)
- 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;
block = le16_to_cpu(index[i].ei_leaf_hi);
block = (block << 32) + le32_to_cpu(index[i].ei_leaf_lo);
- if (ext4fs_devread((lbaint_t)block << log2_blksz, 0, blksz,
- buf))
- ext_block = (struct ext4_extent_header *)buf;
- else
- return 0;
+ 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);
}
}
+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)
{
@@ -1508,54 +1574,22 @@ long int read_allocated_block(struct ext2_inode *inode, int fileblock)
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) {
- 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;
- }
-
- 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;
- }
+ struct ext4_extent_node *node;
- 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;
+ node = ext4fs_extent_cache_get(fileblock);
+ if (!node) {
+ printf("Extent Error\n");
+ return -1;
}
- printf("Extent Error\n");
- free(buf);
- return -1;
+ return fileblock - node->block + node->start;
}
/* Direct blocks. */
diff --git a/fs/ext4/ext4_common.h b/fs/ext4/ext4_common.h
index 5fa1719f2e..a9fd8c6573 100644
--- a/fs/ext4/ext4_common.h
+++ b/fs/ext4/ext4_common.h
@@ -57,6 +57,9 @@ int ext4fs_find_file(const char *path, struct ext2fs_node *rootnode,
int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
struct ext2fs_node **fnode, int *ftype);
+int ext4fs_build_extent_cache(struct ext2_inode *inode);
+void ext4fs_free_extent_cache(void);
+
#if defined(CONFIG_EXT4_WRITE)
uint32_t ext4fs_div_roundup(uint32_t size, uint32_t n);
int ext4fs_checksum_update(unsigned int i);
diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c
index 417ce7b63b..4f1b4c8bce 100644
--- a/fs/ext4/ext4fs.c
+++ b/fs/ext4/ext4fs.c
@@ -63,6 +63,14 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
char *delayed_buf = NULL;
short status;
+ if (le32_to_cpu(node->inode.flags) & EXT4_EXTENTS_FL) {
+ if (ext4fs_build_extent_cache(&node->inode)) {
+ printf("Error building extent cache!\n");
+ len = -1;
+ goto out_exit;
+ }
+ }
+
/* Adjust len so it we can't read past the end of the file. */
if (len > filesize)
len = filesize;
@@ -75,8 +83,10 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
int blockend = blocksize;
int skipfirst = 0;
blknr = read_allocated_block(&(node->inode), i);
- if (blknr < 0)
- return -1;
+ if (blknr < 0) {
+ len = -1;
+ goto out_exit;
+ }
blknr = blknr << log2_fs_blocksize;
@@ -106,8 +116,10 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
delayed_skipfirst,
delayed_extent,
delayed_buf);
- if (status == 0)
- return -1;
+ if (status == 0) {
+ len = -1;
+ goto out_exit;
+ }
previous_block_number = blknr;
delayed_start = blknr;
delayed_extent = blockend;
@@ -132,8 +144,10 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
delayed_skipfirst,
delayed_extent,
delayed_buf);
- if (status == 0)
- return -1;
+ if (status == 0) {
+ len = -1;
+ goto out_exit;
+ }
previous_block_number = -1;
}
memset(buf, 0, blocksize - skipfirst);
@@ -145,11 +159,17 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
status = ext4fs_devread(delayed_start,
delayed_skipfirst, delayed_extent,
delayed_buf);
- if (status == 0)
- return -1;
+ if (status == 0) {
+ len = -1;
+ goto out_exit;
+ }
previous_block_number = -1;
}
+
+out_exit:
+ ext4fs_free_extent_cache();
+
return len;
}
OpenPOWER on IntegriCloud