X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=lib%2Fext2fs%2Fbmap.c;h=65c45c512bbcf5e21b23819cda51e036e143779f;hb=336c440ccea8f94b0728f881cddee84f730e7cc7;hp=16d51e0be5bdb707c38e706694fb54f3753c13e2;hpb=d1154eb460efe588eaed3d439c1caaca149fa362;p=tools%2Fe2fsprogs.git diff --git a/lib/ext2fs/bmap.c b/lib/ext2fs/bmap.c index 16d51e0..65c45c5 100644 --- a/lib/ext2fs/bmap.c +++ b/lib/ext2fs/bmap.c @@ -18,7 +18,7 @@ #include #include "ext2_fs.h" -#include "ext2fs.h" +#include "ext2fsP.h" #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) #define _BMAP_INLINE_ __inline__ @@ -67,7 +67,7 @@ static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, #endif if (!b && (flags & BMAP_ALLOC)) { - b = nr ? ((blk_t *) block_buf)[nr-1] : 0; + b = nr ? ext2fs_le32_to_cpu(((blk_t *)block_buf)[nr - 1]) : ind; retval = ext2fs_alloc_block(fs, b, block_buf + fs->blocksize, &b); if (retval) @@ -95,7 +95,7 @@ static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { - blk_t b; + blk_t b = 0; errcode_t retval; blk_t addr_per_block; @@ -115,7 +115,7 @@ static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { - blk_t b; + blk_t b = 0; errcode_t retval; blk_t addr_per_block; @@ -140,19 +140,27 @@ static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino, static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t handle, - blk64_t block, blk64_t *phys_blk) + blk64_t lblk, blk64_t *phys_blk) { blk64_t base_block, pblock = 0; int i; - if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, - EXT4_FEATURE_RO_COMPAT_BIGALLOC)) + if (!ext2fs_has_feature_bigalloc(fs->super)) return 0; - base_block = block & ~EXT2FS_CLUSTER_MASK(fs); + base_block = lblk & ~EXT2FS_CLUSTER_MASK(fs); + /* + * Except for the logical block (lblk) that was passed in, search all + * blocks in this logical cluster for a mapping to a physical cluster. + * If any such map exists, calculate the physical block that maps to + * the logical block and return that. + * + * The old code wouldn't even look if (block % cluster_ratio) == 0; + * this is incorrect if we're allocating blocks in reverse order. + */ for (i = 0; i < EXT2FS_CLUSTER_RATIO(fs); i++) { - if (block == base_block) - return 0; + if (base_block + i == lblk) + continue; extent_bmap(fs, ino, inode, handle, 0, 0, base_block + i, 0, 0, &pblock); if (pblock) @@ -160,10 +168,38 @@ static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino, } if (pblock == 0) return 0; - *phys_blk = pblock - i + (block - base_block); + *phys_blk = pblock - i + (lblk - base_block); return 0; } +/* Try to map a logical block to an already-allocated physical cluster. */ +errcode_t ext2fs_map_cluster_block(ext2_filsys fs, ext2_ino_t ino, + struct ext2_inode *inode, blk64_t lblk, + blk64_t *pblk) +{ + ext2_extent_handle_t handle; + errcode_t retval; + + /* Need bigalloc and extents to be enabled */ + *pblk = 0; + if (!ext2fs_has_feature_bigalloc(fs->super) || + !(inode->i_flags & EXT4_EXTENTS_FL)) + return 0; + + retval = ext2fs_extent_open2(fs, ino, inode, &handle); + if (retval) + goto out; + + retval = implied_cluster_alloc(fs, ino, inode, handle, lblk, pblk); + if (retval) + goto out2; + +out2: + ext2fs_extent_free(handle); +out: + return retval; +} + static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t handle, @@ -171,22 +207,28 @@ static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino, int *ret_flags, int *blocks_alloc, blk64_t *phys_blk) { + struct blk_alloc_ctx alloc_ctx; struct ext2fs_extent extent; unsigned int offset; errcode_t retval = 0; blk64_t blk64 = 0; int alloc = 0; + int set_flags; + + set_flags = bmap_flags & BMAP_UNINIT ? EXT2_EXTENT_SET_BMAP_UNINIT : 0; if (bmap_flags & BMAP_SET) { retval = ext2fs_extent_set_bmap(handle, block, - *phys_blk, 0); + *phys_blk, set_flags); return retval; } retval = ext2fs_extent_goto(handle, block); if (retval) { /* If the extent is not found, return phys_blk = 0 */ - if (retval == EXT2_ET_EXTENT_NOT_FOUND) + if (retval == EXT2_ET_EXTENT_NOT_FOUND) { + extent.e_lblk = block; goto got_block; + } return retval; } retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); @@ -206,9 +248,13 @@ got_block: retval = extent_bmap(fs, ino, inode, handle, block_buf, 0, block-1, 0, blocks_alloc, &blk64); if (retval) - blk64 = 0; - retval = ext2fs_alloc_block2(fs, blk64, block_buf, - &blk64); + blk64 = ext2fs_find_inode_goal(fs, ino, inode, block); + alloc_ctx.ino = ino; + alloc_ctx.inode = inode; + alloc_ctx.lblk = extent.e_lblk; + alloc_ctx.flags = BLOCK_ALLOC_DATA; + retval = ext2fs_alloc_block3(fs, blk64, block_buf, &blk64, + &alloc_ctx); if (retval) return retval; blk64 &= ~EXT2FS_CLUSTER_MASK(fs); @@ -216,9 +262,11 @@ got_block: alloc++; set_extent: retval = ext2fs_extent_set_bmap(handle, block, - blk64, 0); - if (retval) + blk64, set_flags); + if (retval) { + ext2fs_block_alloc_stats2(fs, blk64, -1); return retval; + } /* Update inode after setting extent */ retval = ext2fs_read_inode(fs, ino, inode); if (retval) @@ -229,6 +277,27 @@ got_block: return 0; } +int ext2fs_file_block_offset_too_big(ext2_filsys fs, + struct ext2_inode *inode, + blk64_t offset) +{ + blk64_t addr_per_block, max_map_block; + + /* Kernel seems to cut us off at 4294967294 blocks */ + if (offset >= (1ULL << 32) - 1) + return 1; + + if (inode->i_flags & EXT4_EXTENTS_FL) + return 0; + + addr_per_block = fs->blocksize >> 2; + max_map_block = addr_per_block; + max_map_block += addr_per_block * addr_per_block; + max_map_block += addr_per_block * addr_per_block * addr_per_block; + max_map_block += 12; + + return offset >= max_map_block; +} errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk64_t block, @@ -238,9 +307,16 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t handle = 0; blk_t addr_per_block; blk_t b, blk32; + blk64_t b64; char *buf = 0; errcode_t retval = 0; int blocks_alloc = 0, inode_dirty = 0; + struct blk_alloc_ctx alloc_ctx = { + .ino = ino, + .inode = inode, + .lblk = 0, + .flags = BLOCK_ALLOC_DATA, + }; if (!(bmap_flags & BMAP_SET)) *phys_blk = 0; @@ -257,6 +333,16 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, } addr_per_block = (blk_t) fs->blocksize >> 2; + if (ext2fs_file_block_offset_too_big(fs, inode, block)) + return EXT2_ET_FILE_TOO_BIG; + + /* + * If an inode has inline data, that means that it doesn't have + * any blocks and we shouldn't map any blocks for it. + */ + if (inode->i_flags & EXT4_INLINE_DATA_FL) + return EXT2_ET_INLINE_DATA_NO_BLOCK; + if (!block_buf) { retval = ext2fs_get_array(2, fs->blocksize, &buf); if (retval) @@ -283,10 +369,14 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, } *phys_blk = inode_bmap(inode, block); - b = block ? inode_bmap(inode, block-1) : 0; + b = block ? inode_bmap(inode, block - 1) : + ext2fs_find_inode_goal(fs, ino, inode, block); if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { - retval = ext2fs_alloc_block(fs, b, block_buf, &b); + b64 = b; + retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64, + &alloc_ctx); + b = b64; if (retval) goto done; inode_bmap(inode, block) = b; @@ -309,7 +399,10 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, } b = inode_bmap(inode, EXT2_IND_BLOCK-1); - retval = ext2fs_alloc_block(fs, b, block_buf, &b); + b64 = b; + retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64, + &alloc_ctx); + b = b64; if (retval) goto done; inode_bmap(inode, EXT2_IND_BLOCK) = b; @@ -334,7 +427,10 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, } b = inode_bmap(inode, EXT2_IND_BLOCK); - retval = ext2fs_alloc_block(fs, b, block_buf, &b); + b64 = b; + retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64, + &alloc_ctx); + b = b64; if (retval) goto done; inode_bmap(inode, EXT2_DIND_BLOCK) = b; @@ -358,7 +454,10 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, } b = inode_bmap(inode, EXT2_DIND_BLOCK); - retval = ext2fs_alloc_block(fs, b, block_buf, &b); + b64 = b; + retval = ext2fs_alloc_block3(fs, b64, block_buf, &b64, + &alloc_ctx); + b = b64; if (retval) goto done; inode_bmap(inode, EXT2_TIND_BLOCK) = b; @@ -369,6 +468,8 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, if (retval == 0) *phys_blk = blk32; done: + if (*phys_blk && retval == 0 && (bmap_flags & BMAP_ZERO)) + retval = ext2fs_zero_blocks2(fs, *phys_blk, 1, NULL, NULL); if (buf) ext2fs_free_mem(&buf); if (handle)