/* * The logical block -> physical block routine. * * Copyright (C) 2009 Liu Aleaxander -- All rights reserved. This file * may be redistributed under the terms of the GNU Public License. */ #include #include #include #include #include #include "ext2_fs.h" static const struct ext4_extent_header * ext4_find_leaf(struct fs_info *fs, const struct ext4_extent_header *eh, block_t block) { struct ext4_extent_idx *index; block_t blk; int i; while (1) { if (eh->eh_magic != EXT4_EXT_MAGIC) return NULL; if (eh->eh_depth == 0) return eh; index = EXT4_FIRST_INDEX(eh); for (i = 0; i < (int)eh->eh_entries; i++) { if (block < index[i].ei_block) break; } if (--i < 0) return NULL; blk = index[i].ei_leaf_hi; blk = (blk << 32) + index[i].ei_leaf_lo; eh = get_cache(fs->fs_dev, blk); } } /* handle the ext4 extents to get the phsical block number */ /* 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; const struct ext4_extent *ext; int i; block_t start; leaf = ext4_find_leaf(fs, &PVT(inode)->i_extent_hdr, block); if (!leaf) { printf("ERROR, extent leaf not found\n"); return 0; } ext = EXT4_FIRST_EXTENT(leaf); for (i = 0; i < leaf->eh_entries; i++) { if (block < ext[i].ee_block) break; } if (--i < 0) { printf("ERROR, not find the right block\n"); return 0; } /* got it */ block -= ext[i].ee_block; if (block >= ext[i].ee_len) return 0; 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, size_t *nblocks) { int addr_shift = BLOCK_SHIFT(fs) - 2; 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_count - 1); start = blk[index]; } 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, size_t *nblocks) { struct fs_info *fs = inode->fs; const uint32_t addr_per_block = BLOCK_SIZE(fs) >> 2; const int shft_per_block = BLOCK_SHIFT(fs) - 2; const uint32_t direct_blocks = EXT2_NDIR_BLOCKS; const uint32_t indirect_blocks = addr_per_block; const uint32_t double_blocks = addr_per_block << shft_per_block; const uint32_t triple_blocks = double_blocks << shft_per_block; /* direct blocks */ if (block < direct_blocks) 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, nblocks); /* double indirect blocks */ block -= indirect_blocks; if (block < double_blocks) return bmap_indirect(fs, PVT(inode)->i_block[EXT2_DIND_BLOCK], 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, nblocks); /* This can't happen... */ return 0; } /** * Map the logical block to physic block where the file data stores. * 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. * @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, size_t *nblocks) { block_t ret; if (inode->flags & EXT4_EXTENTS_FLAG) ret = bmap_extent(inode, block, nblocks); else ret = bmap_traditional(inode, block, nblocks); return ret; } /* * Next extent for getfssec */ int ext2_next_extent(struct inode *inode, uint32_t lstart) { 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, lstart >> blktosec, &nblocks); if (!block) inode->next_extent.pstart = EXTENT_ZERO; else inode->next_extent.pstart = ((sector_t)block << blktosec) | (lstart & blkmask); inode->next_extent.len = (nblocks << blktosec) - (lstart & blkmask); return 0; }