bmap.c revision 8e8a190fa212ca28efb2e8a8b3318a24d7fc4b7a
1/* 2 * bmap.c --- logical to physical block mapping 3 * 4 * Copyright (C) 1997 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include <stdio.h> 13#include <string.h> 14#if HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17#include <errno.h> 18 19#include "ext2_fs.h" 20#include "ext2fs.h" 21 22#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) 23#define _BMAP_INLINE_ __inline__ 24#else 25#define _BMAP_INLINE_ 26#endif 27 28extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, 29 struct ext2_inode *inode, 30 char *block_buf, int bmap_flags, 31 blk_t block, blk_t *phys_blk); 32 33#define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) 34 35static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 36 blk_t ind, char *block_buf, 37 int *blocks_alloc, 38 blk_t nr, blk_t *ret_blk) 39{ 40 errcode_t retval; 41 blk_t b; 42 43 if (!ind) { 44 if (flags & BMAP_SET) 45 return EXT2_ET_SET_BMAP_NO_IND; 46 *ret_blk = 0; 47 return 0; 48 } 49 retval = io_channel_read_blk(fs->io, ind, 1, block_buf); 50 if (retval) 51 return retval; 52 53 if (flags & BMAP_SET) { 54 b = *ret_blk; 55#ifdef WORDS_BIGENDIAN 56 b = ext2fs_swab32(b); 57#endif 58 ((blk_t *) block_buf)[nr] = b; 59 return io_channel_write_blk(fs->io, ind, 1, block_buf); 60 } 61 62 b = ((blk_t *) block_buf)[nr]; 63 64#ifdef WORDS_BIGENDIAN 65 b = ext2fs_swab32(b); 66#endif 67 68 if (!b && (flags & BMAP_ALLOC)) { 69 b = nr ? ((blk_t *) block_buf)[nr-1] : 0; 70 retval = ext2fs_alloc_block(fs, b, 71 block_buf + fs->blocksize, &b); 72 if (retval) 73 return retval; 74 75#ifdef WORDS_BIGENDIAN 76 ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); 77#else 78 ((blk_t *) block_buf)[nr] = b; 79#endif 80 81 retval = io_channel_write_blk(fs->io, ind, 1, block_buf); 82 if (retval) 83 return retval; 84 85 (*blocks_alloc)++; 86 } 87 88 *ret_blk = b; 89 return 0; 90} 91 92static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, 93 blk_t dind, char *block_buf, 94 int *blocks_alloc, 95 blk_t nr, blk_t *ret_blk) 96{ 97 blk_t b; 98 errcode_t retval; 99 blk_t addr_per_block; 100 101 addr_per_block = (blk_t) fs->blocksize >> 2; 102 103 retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, 104 blocks_alloc, nr / addr_per_block, &b); 105 if (retval) 106 return retval; 107 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 108 nr % addr_per_block, ret_blk); 109 return retval; 110} 111 112static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, 113 blk_t tind, char *block_buf, 114 int *blocks_alloc, 115 blk_t nr, blk_t *ret_blk) 116{ 117 blk_t b; 118 errcode_t retval; 119 blk_t addr_per_block; 120 121 addr_per_block = (blk_t) fs->blocksize >> 2; 122 123 retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, 124 blocks_alloc, nr / addr_per_block, &b); 125 if (retval) 126 return retval; 127 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 128 nr % addr_per_block, ret_blk); 129 return retval; 130} 131 132static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino, 133 struct ext2_inode *inode, 134 ext2_extent_handle_t handle, 135 char *block_buf, int bmap_flags, blk64_t block, 136 int *ret_flags, int *blocks_alloc, 137 blk64_t *phys_blk); 138 139static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino, 140 struct ext2_inode *inode, 141 ext2_extent_handle_t handle, 142 blk64_t block, blk64_t *phys_blk) 143{ 144 blk64_t base_block, pblock = 0; 145 int i; 146 147 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 148 EXT4_FEATURE_RO_COMPAT_BIGALLOC)) 149 return 0; 150 151 base_block = block & ~EXT2FS_CLUSTER_MASK(fs); 152 for (i = 0; i < EXT2FS_CLUSTER_RATIO(fs); i++) { 153 if (block == base_block) 154 return 0; 155 extent_bmap(fs, ino, inode, handle, 0, 0, 156 base_block + i, 0, 0, &pblock); 157 if (pblock) 158 break; 159 } 160 if (pblock == 0) 161 return 0; 162 *phys_blk = pblock - i + (block - base_block); 163 return 0; 164} 165 166static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino, 167 struct ext2_inode *inode, 168 ext2_extent_handle_t handle, 169 char *block_buf, int bmap_flags, blk64_t block, 170 int *ret_flags, int *blocks_alloc, 171 blk64_t *phys_blk) 172{ 173 struct ext2fs_extent extent; 174 unsigned int offset; 175 errcode_t retval = 0; 176 blk64_t blk64 = 0; 177 int alloc = 0; 178 179 if (bmap_flags & BMAP_SET) { 180 retval = ext2fs_extent_set_bmap(handle, block, 181 *phys_blk, 0); 182 return retval; 183 } 184 retval = ext2fs_extent_goto(handle, block); 185 if (retval) { 186 /* If the extent is not found, return phys_blk = 0 */ 187 if (retval == EXT2_ET_EXTENT_NOT_FOUND) 188 goto got_block; 189 return retval; 190 } 191 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); 192 if (retval) 193 return retval; 194 offset = block - extent.e_lblk; 195 if (block >= extent.e_lblk && (offset <= extent.e_len)) { 196 *phys_blk = extent.e_pblk + offset; 197 if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) 198 *ret_flags |= BMAP_RET_UNINIT; 199 } 200got_block: 201 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { 202 implied_cluster_alloc(fs, ino, inode, handle, block, &blk64); 203 if (blk64) 204 goto set_extent; 205 retval = extent_bmap(fs, ino, inode, handle, block_buf, 206 0, block-1, 0, blocks_alloc, &blk64); 207 if (retval) 208 blk64 = 0; 209 retval = ext2fs_alloc_block2(fs, blk64, block_buf, 210 &blk64); 211 if (retval) 212 return retval; 213 blk64 &= ~EXT2FS_CLUSTER_MASK(fs); 214 blk64 += EXT2FS_CLUSTER_MASK(fs) & block; 215 alloc++; 216 set_extent: 217 retval = ext2fs_extent_set_bmap(handle, block, 218 blk64, 0); 219 if (retval) 220 return retval; 221 /* Update inode after setting extent */ 222 retval = ext2fs_read_inode(fs, ino, inode); 223 if (retval) 224 return retval; 225 *blocks_alloc += alloc; 226 *phys_blk = blk64; 227 } 228 return 0; 229} 230 231 232errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, 233 char *block_buf, int bmap_flags, blk64_t block, 234 int *ret_flags, blk64_t *phys_blk) 235{ 236 struct ext2_inode inode_buf; 237 ext2_extent_handle_t handle = 0; 238 blk_t addr_per_block; 239 blk_t b, blk32; 240 char *buf = 0; 241 errcode_t retval = 0; 242 int blocks_alloc = 0, inode_dirty = 0; 243 244 if (!(bmap_flags & BMAP_SET)) 245 *phys_blk = 0; 246 247 if (ret_flags) 248 *ret_flags = 0; 249 250 /* Read inode structure if necessary */ 251 if (!inode) { 252 retval = ext2fs_read_inode(fs, ino, &inode_buf); 253 if (retval) 254 return retval; 255 inode = &inode_buf; 256 } 257 addr_per_block = (blk_t) fs->blocksize >> 2; 258 259 if (!block_buf) { 260 retval = ext2fs_get_array(2, fs->blocksize, &buf); 261 if (retval) 262 return retval; 263 block_buf = buf; 264 } 265 266 if (inode->i_flags & EXT4_EXTENTS_FL) { 267 retval = ext2fs_extent_open2(fs, ino, inode, &handle); 268 if (retval) 269 goto done; 270 retval = extent_bmap(fs, ino, inode, handle, block_buf, 271 bmap_flags, block, ret_flags, 272 &blocks_alloc, phys_blk); 273 goto done; 274 } 275 276 if (block < EXT2_NDIR_BLOCKS) { 277 if (bmap_flags & BMAP_SET) { 278 b = *phys_blk; 279 inode_bmap(inode, block) = b; 280 inode_dirty++; 281 goto done; 282 } 283 284 *phys_blk = inode_bmap(inode, block); 285 b = block ? inode_bmap(inode, block-1) : 0; 286 287 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { 288 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 289 if (retval) 290 goto done; 291 inode_bmap(inode, block) = b; 292 blocks_alloc++; 293 *phys_blk = b; 294 } 295 goto done; 296 } 297 298 /* Indirect block */ 299 block -= EXT2_NDIR_BLOCKS; 300 blk32 = *phys_blk; 301 if (block < addr_per_block) { 302 b = inode_bmap(inode, EXT2_IND_BLOCK); 303 if (!b) { 304 if (!(bmap_flags & BMAP_ALLOC)) { 305 if (bmap_flags & BMAP_SET) 306 retval = EXT2_ET_SET_BMAP_NO_IND; 307 goto done; 308 } 309 310 b = inode_bmap(inode, EXT2_IND_BLOCK-1); 311 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 312 if (retval) 313 goto done; 314 inode_bmap(inode, EXT2_IND_BLOCK) = b; 315 blocks_alloc++; 316 } 317 retval = block_ind_bmap(fs, bmap_flags, b, block_buf, 318 &blocks_alloc, block, &blk32); 319 if (retval == 0) 320 *phys_blk = blk32; 321 goto done; 322 } 323 324 /* Doubly indirect block */ 325 block -= addr_per_block; 326 if (block < addr_per_block * addr_per_block) { 327 b = inode_bmap(inode, EXT2_DIND_BLOCK); 328 if (!b) { 329 if (!(bmap_flags & BMAP_ALLOC)) { 330 if (bmap_flags & BMAP_SET) 331 retval = EXT2_ET_SET_BMAP_NO_IND; 332 goto done; 333 } 334 335 b = inode_bmap(inode, EXT2_IND_BLOCK); 336 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 337 if (retval) 338 goto done; 339 inode_bmap(inode, EXT2_DIND_BLOCK) = b; 340 blocks_alloc++; 341 } 342 retval = block_dind_bmap(fs, bmap_flags, b, block_buf, 343 &blocks_alloc, block, &blk32); 344 if (retval == 0) 345 *phys_blk = blk32; 346 goto done; 347 } 348 349 /* Triply indirect block */ 350 block -= addr_per_block * addr_per_block; 351 b = inode_bmap(inode, EXT2_TIND_BLOCK); 352 if (!b) { 353 if (!(bmap_flags & BMAP_ALLOC)) { 354 if (bmap_flags & BMAP_SET) 355 retval = EXT2_ET_SET_BMAP_NO_IND; 356 goto done; 357 } 358 359 b = inode_bmap(inode, EXT2_DIND_BLOCK); 360 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 361 if (retval) 362 goto done; 363 inode_bmap(inode, EXT2_TIND_BLOCK) = b; 364 blocks_alloc++; 365 } 366 retval = block_tind_bmap(fs, bmap_flags, b, block_buf, 367 &blocks_alloc, block, &blk32); 368 if (retval == 0) 369 *phys_blk = blk32; 370done: 371 if (buf) 372 ext2fs_free_mem(&buf); 373 if (handle) 374 ext2fs_extent_free(handle); 375 if ((retval == 0) && (blocks_alloc || inode_dirty)) { 376 ext2fs_iblk_add_blocks(fs, inode, blocks_alloc); 377 retval = ext2fs_write_inode(fs, ino, inode); 378 } 379 return retval; 380} 381 382errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, 383 char *block_buf, int bmap_flags, blk_t block, 384 blk_t *phys_blk) 385{ 386 errcode_t ret; 387 blk64_t ret_blk = *phys_blk; 388 389 ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block, 390 0, &ret_blk); 391 if (ret) 392 return ret; 393 if (ret_blk >= ((long long) 1 << 32)) 394 return EOVERFLOW; 395 *phys_blk = ret_blk; 396 return 0; 397} 398