summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/fs/ext2/bmap.c110
-rw-r--r--core/fs/ext2/ext2.c146
-rw-r--r--core/fs/ext2/ext2_fs.h3
3 files changed, 109 insertions, 150 deletions
diff --git a/core/fs/ext2/bmap.c b/core/fs/ext2/bmap.c
index 73473e22..6940c1d5 100644
--- a/core/fs/ext2/bmap.c
+++ b/core/fs/ext2/bmap.c
@@ -41,7 +41,9 @@ ext4_find_leaf(struct fs_info *fs, const struct ext4_extent_header *eh,
}
/* handle the ext4 extents to get the phsical block number */
-static block_t bmap_extent(struct inode *inode, block_t block)
+/* XXX: still need to handle sparse files with extents */
+static block_t
+bmap_extent(struct inode *inode, uint32_t block, size_t *nblocks)
{
struct fs_info *fs = inode->fs;
const struct ext4_extent_header *leaf;
@@ -69,38 +71,77 @@ static block_t bmap_extent(struct inode *inode, block_t block)
block -= ext[i].ee_block;
if (block >= ext[i].ee_len)
return 0;
- start = ext[i].ee_start_hi;
- start = (start << 32) + ext[i].ee_start_lo;
+ start = ((block_t)ext[i].ee_start_hi << 32) + ext[i].ee_start_lo;
+
+ if (nblocks)
+ *nblocks = ext[i].ee_len - block;
return start + block;
}
/*
+ * Scan forward in a range of blocks to see if they are contiguous,
+ * then return the initial value.
+ */
+static uint32_t
+scan_set_nblocks(const uint32_t *map, unsigned int count, size_t *nblocks)
+{
+ uint32_t blk = *map;
+
+ if (nblocks) {
+ uint32_t skip = blk ? 1 : 0;
+ uint32_t next = blk + skip;
+ size_t cnt = 1;
+
+ while (--count) {
+ map++;
+ if (*map == next) {
+ cnt++;
+ next += skip;
+ } else {
+ break;
+ }
+ }
+
+ *nblocks = cnt;
+ }
+
+ return blk;
+}
+
+/*
* The actual indirect block map handling - the block passed in should
* be relative to the beginning of the particular block hierarchy.
*/
-static block_t bmap_indirect(struct fs_info *fs, uint32_t start,
- uint32_t block, int levels)
+static block_t
+bmap_indirect(struct fs_info *fs, uint32_t start, uint32_t block,
+ int levels, size_t *nblocks)
{
int addr_shift = BLOCK_SHIFT(fs) - 2;
- uint32_t addr_mask = (1 << addr_shift)-1;
- uint32_t index;
- const uint32_t *blk;
+ uint32_t addr_count = 1 << addr_shift;
+ const uint32_t *blk = NULL;
+ uint32_t index = 0;
while (levels--) {
+ if (!start) {
+ if (nblocks)
+ *nblocks = addr_count << (levels * addr_shift);
+ return 0;
+ }
blk = get_cache(fs->fs_dev, start);
- index = (block >> (levels * addr_shift)) & addr_mask;
+ index = (block >> (levels * addr_shift)) & (addr_count - 1);
start = blk[index];
}
- return start;
+ return scan_set_nblocks(blk + index, addr_count - index, nblocks);
}
/*
* Handle the traditional block map, like indirect, double indirect
* and triple indirect
*/
-static block_t bmap_traditional(struct inode *inode, block_t block)
+static block_t
+bmap_traditional(struct inode *inode, block_t block, size_t *nblocks)
{
struct fs_info *fs = inode->fs;
const uint32_t addr_per_block = BLOCK_SIZE(fs) >> 2;
@@ -112,25 +153,26 @@ static block_t bmap_traditional(struct inode *inode, block_t block)
/* direct blocks */
if (block < direct_blocks)
- return PVT(inode)->i_block[block];
+ return scan_set_nblocks(&PVT(inode)->i_block[block],
+ direct_blocks - block, nblocks);
/* indirect blocks */
block -= direct_blocks;
if (block < indirect_blocks)
return bmap_indirect(fs, PVT(inode)->i_block[EXT2_IND_BLOCK],
- block, 1);
+ block, 1, nblocks);
/* double indirect blocks */
block -= indirect_blocks;
if (block < double_blocks)
return bmap_indirect(fs, PVT(inode)->i_block[EXT2_DIND_BLOCK],
- block, 2);
+ block, 2, nblocks);
/* triple indirect block */
block -= double_blocks;
if (block < triple_blocks)
return bmap_indirect(fs, PVT(inode)->i_block[EXT2_TIND_BLOCK],
- block, 3);
+ block, 3, nblocks);
/* This can't happen... */
return 0;
@@ -142,20 +184,44 @@ static block_t bmap_traditional(struct inode *inode, block_t block)
* In EXT4, there are two ways to handle the map process, extents and indirect.
* EXT4 uses a inode flag to mark extent file and indirect block file.
*
- * @fs: the fs_info structure.
- * @inode: the inode structure.
- * @block: the logical block to be mapped.
- * @retrun: the physical block number.
+ * @fs: the fs_info structure.
+ * @inode: the inode structure.
+ * @block: the logical block to be mapped.
+ * @nblocks: optional pointer to number of contiguous blocks (low estimate)
+ * @retrun: the physical block number.
*
*/
-block_t ext2_bmap(struct inode *inode, block_t block)
+block_t ext2_bmap(struct inode *inode, block_t block, size_t *nblocks)
{
block_t ret;
if (inode->flags & EXT4_EXTENTS_FLAG)
- ret = bmap_extent(inode, block);
+ ret = bmap_extent(inode, block, nblocks);
else
- ret = bmap_traditional(inode, block);
+ ret = bmap_traditional(inode, block, nblocks);
return ret;
}
+
+
+/*
+ * Next extent for getfssec
+ */
+void ext2_next_extent(struct inode *inode)
+{
+ struct fs_info *fs = inode->fs;
+ int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
+ int blkmask = (1 << blktosec) - 1;
+ block_t block;
+ size_t nblocks = 0;
+
+ block = ext2_bmap(inode, inode->next_extent.lstart >> blktosec, &nblocks);
+
+ if (!block)
+ inode->next_extent.pstart = EXTENT_ZERO;
+ else
+ inode->next_extent.pstart = ((sector_t)block << blktosec) |
+ (inode->next_extent.lstart & blkmask);
+
+ inode->next_extent.len = nblocks << blktosec;
+}
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
index 29d03712..f1853d3e 100644
--- a/core/fs/ext2/ext2.c
+++ b/core/fs/ext2/ext2.c
@@ -12,8 +12,8 @@
/*
* get the group's descriptor of group_num
*/
-const struct ext2_group_desc *ext2_get_group_desc(struct fs_info *fs,
- uint32_t group_num)
+static const struct ext2_group_desc *
+ext2_get_group_desc(struct fs_info *fs, uint32_t group_num)
{
struct ext2_sb_info *sbi = EXT2_SB(fs);
uint32_t desc_block, desc_index;
@@ -37,117 +37,6 @@ const struct ext2_group_desc *ext2_get_group_desc(struct fs_info *fs,
return &desc_data_block[desc_index];
}
-
-
-/**
- * linsector:
- *
- * Convert a linear sector index in a file to linear sector number
- *
- * well, alought this function converts a linear sector number to
- * physic sector number, it uses block cache in the implemention.
- *
- * @param: lin_sector, the lineral sector index
- *
- * @return: physic sector number
- */
-static sector_t linsector(struct inode *inode, uint32_t lin_sector)
-{
- struct fs_info *fs = inode->fs;
- int blk_bits = fs->block_shift - fs->sector_shift;
- block_t block = ext2_bmap(inode, lin_sector >> blk_bits);
-
- return (block << blk_bits) + (lin_sector & ((1 << blk_bits) - 1));
-}
-
-
-/**
- * getlinsec_ext:
- *
- * same as getlinsec, except load any sector from the zero
- * block as all zeros; use to load any data derived from
- * n ext2 block pointer, i.e. anything *except the superblock
- *
- */
-static void getlinsec_ext(struct fs_info *fs, char *buf,
- sector_t sector, int sector_cnt)
-{
- int ext_cnt = 0;
- int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
- struct disk *disk = fs->fs_dev->disk;
-
- if (sector < sec_per_block) {
- ext_cnt = sec_per_block - sector;
- memset(buf, 0, ext_cnt << fs->sector_shift);
- buf += ext_cnt << fs->sector_shift;
- }
-
- sector += ext_cnt;
- sector_cnt -= ext_cnt;
- disk->rdwr_sectors(disk, buf, sector, sector_cnt, 0);
-}
-
-/*
- * Get multiple sectors from a file
- *
- * Alought we have made the buffer data based on block size,
- * we use sector for implemention; because reading multiple
- * sectors (then can be multiple blocks) is what the function
- * do. So, let it be based on sectors.
- *
- */
-static uint32_t ext2_getfssec(struct file *file, char *buf,
- int sectors, bool *have_more)
-{
- struct inode *inode = file->inode;
- struct fs_info *fs = file->fs;
- int sector_left, next_sector, sector_idx;
- int frag_start, con_sec_cnt;
- int bytes_read = sectors << fs->sector_shift;
- uint32_t bytesleft = inode->size - file->offset;
-
- sector_left = (bytesleft + SECTOR_SIZE(fs) - 1) >> fs->sector_shift;
- if (sectors > sector_left)
- sectors = sector_left;
-
- sector_idx = file->offset >> fs->sector_shift;
- while (sectors) {
- /*
- * get the frament
- */
- next_sector = frag_start = linsector(inode, sector_idx);
- con_sec_cnt = 0;
-
- /* get the consective sectors count */
- do {
- con_sec_cnt++;
- sectors--;
- if (sectors <= 0)
- break;
-
- sector_idx++;
- next_sector++;
- } while (next_sector == linsector(inode, sector_idx));
-
-#if 0
- printf("You are reading data stored at sector --0x%x--0x%x\n",
- frag_start, frag_start + con_sec_cnt -1);
-#endif
- getlinsec_ext(fs, buf, frag_start, con_sec_cnt);
- buf += con_sec_cnt << fs->sector_shift;
- } while(sectors);
-
- if (bytes_read >= bytesleft) {
- bytes_read = bytesleft;
- *have_more = 0;
- } else {
- *have_more = 1;
- }
- file->offset += bytes_read;
-
- return bytes_read;
-}
-
/*
* Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure.
*/
@@ -171,21 +60,29 @@ static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p)
}
/*
+ * Map a logical sector and load it into the cache
+ */
+static const void *
+ext2_get_cache(struct inode *inode, block_t lblock)
+{
+ block_t pblock = ext2_bmap(inode, lblock, NULL);
+ return get_cache(inode->fs->fs_dev, pblock);
+}
+
+/*
* find a dir entry, return it if found, or return NULL.
*/
static const struct ext2_dir_entry *
ext2_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
{
block_t index = 0;
- block_t block;
uint32_t i = 0, offset, maxoffset;
const struct ext2_dir_entry *de;
const char *data;
size_t dname_len = strlen(dname);
while (i < inode->size) {
- block = ext2_bmap(inode, index++);
- data = get_cache(fs->fs_dev, block);
+ data = ext2_get_cache(inode, index++);
offset = 0;
maxoffset = min(BLOCK_SIZE(fs), i-inode->size);
@@ -294,9 +191,7 @@ static struct inode *ext2_iget(const char *dname, struct inode *parent)
static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
{
struct fs_info *fs = inode->fs;
- struct device *dev = fs->fs_dev;
size_t block_size = BLOCK_SIZE(fs);
- block_t block;
uint32_t index = 0; /* Logical block number */
size_t chunk;
const char *data;
@@ -307,8 +202,7 @@ static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
while (bytes) {
chunk = min(bytes, block_size);
- block = ext2_bmap(inode, index++);
- data = get_cache(dev, block);
+ data = ext2_get_cache(inode, index++);
memcpy(p, data, chunk);
bytes -= chunk;
@@ -318,7 +212,7 @@ static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
return 0;
}
-int ext2_readlink(struct inode *inode, char *buf)
+static int ext2_readlink(struct inode *inode, char *buf)
{
struct fs_info *fs = inode->fs;
int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
@@ -347,14 +241,11 @@ static struct dirent *ext2_readdir(struct file *file)
const struct ext2_dir_entry *de;
const char *data;
block_t index = file->offset >> fs->block_shift;
- block_t block;
if (file->offset >= inode->size)
return NULL; /* End of file */
- block = ext2_bmap(inode, index);
-
- data = get_cache(fs->fs_dev, block);
+ data = ext2_get_cache(inode, index);
de = (const struct ext2_dir_entry *)
(data + (file->offset & (BLOCK_SIZE(fs) - 1)));
@@ -434,7 +325,7 @@ const struct fs_ops ext2_fs_ops = {
.fs_flags = FS_THISIND | FS_USEMEM,
.fs_init = ext2_fs_init,
.searchdir = NULL,
- .getfssec = ext2_getfssec,
+ .getfssec = generic_getfssec,
.close_file = generic_close_file,
.mangle_name = generic_mangle_name,
.unmangle_name = generic_unmangle_name,
@@ -442,5 +333,6 @@ const struct fs_ops ext2_fs_ops = {
.iget_root = ext2_iget_root,
.iget = ext2_iget,
.readlink = ext2_readlink,
- .readdir = ext2_readdir
+ .readdir = ext2_readdir,
+ .next_extent = ext2_next_extent,
};
diff --git a/core/fs/ext2/ext2_fs.h b/core/fs/ext2/ext2_fs.h
index 8d276097..f2660919 100644
--- a/core/fs/ext2/ext2_fs.h
+++ b/core/fs/ext2/ext2_fs.h
@@ -304,6 +304,7 @@ struct ext2_pvt_inode {
/*
* functions
*/
-block_t ext2_bmap(struct inode *, block_t);
+block_t ext2_bmap(struct inode *, block_t, size_t *);
+void ext2_next_extent(struct inode *);
#endif /* ext2_fs.h */