diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 02da75c084048396085fb9c288882c906c5b0e18..6584892dd28de4d0a5f5aa31e230fe3b5ebb0293 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #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 5fa1719f2eebdcaf5107690040d909ee56e1e566..a9fd8c65730ba70db7b11ff3e0ced0ad1e98990a 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 417ce7b63bf0215122821cee7236e0062e9ee7ef..4f1b4c8bce9547c935dc18ff65874322be67a1a9 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; }