diff options
-rw-r--r-- | core/fs/ext2/bmap.c | 110 | ||||
-rw-r--r-- | core/fs/ext2/ext2.c | 146 | ||||
-rw-r--r-- | core/fs/ext2/ext2_fs.h | 3 |
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 */ |