1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "ext4_utils.h" 18 #include "ext4.h" 19 #include "ext4_extents.h" 20 #include "indirect.h" 21 #include "allocate.h" 22 23 #include <sparse/sparse.h> 24 25 #include <stdlib.h> 26 #include <stdio.h> 27 28 /* Creates data buffers for the first backing_len bytes of a block allocation 29 and queues them to be written */ 30 static u8 *create_backing(struct block_allocation *alloc, 31 unsigned long backing_len) 32 { 33 if (DIV_ROUND_UP(backing_len, info.block_size) > EXT4_NDIR_BLOCKS) 34 critical_error("indirect backing larger than %d blocks", EXT4_NDIR_BLOCKS); 35 36 u8 *data = calloc(backing_len, 1); 37 if (!data) 38 critical_error_errno("calloc"); 39 40 u8 *ptr = data; 41 for (; alloc != NULL && backing_len > 0; get_next_region(alloc)) { 42 u32 region_block; 43 u32 region_len; 44 u32 len; 45 get_region(alloc, ®ion_block, ®ion_len); 46 47 len = min(region_len * info.block_size, backing_len); 48 49 sparse_file_add_data(info.sparse_file, ptr, len, region_block); 50 ptr += len; 51 backing_len -= len; 52 } 53 54 return data; 55 } 56 57 static void reserve_indirect_block(struct block_allocation *alloc, int len) 58 { 59 if (reserve_oob_blocks(alloc, 1)) { 60 error("failed to reserve oob block"); 61 return; 62 } 63 64 if (advance_blocks(alloc, len)) { 65 error("failed to advance %d blocks", len); 66 return; 67 } 68 } 69 70 static void reserve_dindirect_block(struct block_allocation *alloc, int len) 71 { 72 if (reserve_oob_blocks(alloc, 1)) { 73 error("failed to reserve oob block"); 74 return; 75 } 76 77 while (len > 0) { 78 int ind_block_len = min((int)aux_info.blocks_per_ind, len); 79 80 reserve_indirect_block(alloc, ind_block_len); 81 82 len -= ind_block_len; 83 } 84 85 } 86 87 static void reserve_tindirect_block(struct block_allocation *alloc, int len) 88 { 89 if (reserve_oob_blocks(alloc, 1)) { 90 error("failed to reserve oob block"); 91 return; 92 } 93 94 while (len > 0) { 95 int dind_block_len = min((int)aux_info.blocks_per_dind, len); 96 97 reserve_dindirect_block(alloc, dind_block_len); 98 99 len -= dind_block_len; 100 } 101 } 102 103 static void fill_indirect_block(u32 *ind_block, int len, struct block_allocation *alloc) 104 { 105 int i; 106 for (i = 0; i < len; i++) { 107 ind_block[i] = get_block(alloc, i); 108 } 109 } 110 111 static void fill_dindirect_block(u32 *dind_block, int len, struct block_allocation *alloc) 112 { 113 int i; 114 u32 ind_block; 115 116 for (i = 0; len > 0; i++) { 117 ind_block = get_oob_block(alloc, 0); 118 if (advance_oob_blocks(alloc, 1)) { 119 error("failed to reserve oob block"); 120 return; 121 } 122 123 dind_block[i] = ind_block; 124 125 u32 *ind_block_data = calloc(info.block_size, 1); 126 sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size, 127 ind_block); 128 int ind_block_len = min((int)aux_info.blocks_per_ind, len); 129 130 fill_indirect_block(ind_block_data, ind_block_len, alloc); 131 132 if (advance_blocks(alloc, ind_block_len)) { 133 error("failed to advance %d blocks", ind_block_len); 134 return; 135 } 136 137 len -= ind_block_len; 138 } 139 } 140 141 static void fill_tindirect_block(u32 *tind_block, int len, struct block_allocation *alloc) 142 { 143 int i; 144 u32 dind_block; 145 146 for (i = 0; len > 0; i++) { 147 dind_block = get_oob_block(alloc, 0); 148 if (advance_oob_blocks(alloc, 1)) { 149 error("failed to reserve oob block"); 150 return; 151 } 152 153 tind_block[i] = dind_block; 154 155 u32 *dind_block_data = calloc(info.block_size, 1); 156 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size, 157 dind_block); 158 int dind_block_len = min((int)aux_info.blocks_per_dind, len); 159 160 fill_dindirect_block(dind_block_data, dind_block_len, alloc); 161 162 len -= dind_block_len; 163 } 164 } 165 166 /* Given an allocation, attach as many blocks as possible to direct inode 167 blocks, and return the rest */ 168 static int inode_attach_direct_blocks(struct ext4_inode *inode, 169 struct block_allocation *alloc, u32 *block_len) 170 { 171 int len = min(*block_len, EXT4_NDIR_BLOCKS); 172 int i; 173 174 for (i = 0; i < len; i++) { 175 inode->i_block[i] = get_block(alloc, i); 176 } 177 178 if (advance_blocks(alloc, len)) { 179 error("failed to advance %d blocks", len); 180 return -1; 181 } 182 183 *block_len -= len; 184 return 0; 185 } 186 187 /* Given an allocation, attach as many blocks as possible to indirect blocks, 188 and return the rest 189 Assumes that the blocks necessary to hold the indirect blocks were included 190 as part of the allocation */ 191 static int inode_attach_indirect_blocks(struct ext4_inode *inode, 192 struct block_allocation *alloc, u32 *block_len) 193 { 194 int len = min(*block_len, aux_info.blocks_per_ind); 195 196 int ind_block = get_oob_block(alloc, 0); 197 inode->i_block[EXT4_IND_BLOCK] = ind_block; 198 199 if (advance_oob_blocks(alloc, 1)) { 200 error("failed to advance oob block"); 201 return -1; 202 } 203 204 u32 *ind_block_data = calloc(info.block_size, 1); 205 sparse_file_add_data(info.sparse_file, ind_block_data, info.block_size, 206 ind_block); 207 208 fill_indirect_block(ind_block_data, len, alloc); 209 210 if (advance_blocks(alloc, len)) { 211 error("failed to advance %d blocks", len); 212 return -1; 213 } 214 215 *block_len -= len; 216 return 0; 217 } 218 219 /* Given an allocation, attach as many blocks as possible to doubly indirect 220 blocks, and return the rest. 221 Assumes that the blocks necessary to hold the indirect and doubly indirect 222 blocks were included as part of the allocation */ 223 static int inode_attach_dindirect_blocks(struct ext4_inode *inode, 224 struct block_allocation *alloc, u32 *block_len) 225 { 226 int len = min(*block_len, aux_info.blocks_per_dind); 227 228 int dind_block = get_oob_block(alloc, 0); 229 inode->i_block[EXT4_DIND_BLOCK] = dind_block; 230 231 if (advance_oob_blocks(alloc, 1)) { 232 error("failed to advance oob block"); 233 return -1; 234 } 235 236 u32 *dind_block_data = calloc(info.block_size, 1); 237 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size, 238 dind_block); 239 240 fill_dindirect_block(dind_block_data, len, alloc); 241 242 if (advance_blocks(alloc, len)) { 243 error("failed to advance %d blocks", len); 244 return -1; 245 } 246 247 *block_len -= len; 248 return 0; 249 } 250 251 /* Given an allocation, attach as many blocks as possible to triply indirect 252 blocks, and return the rest. 253 Assumes that the blocks necessary to hold the indirect, doubly indirect and 254 triply indirect blocks were included as part of the allocation */ 255 static int inode_attach_tindirect_blocks(struct ext4_inode *inode, 256 struct block_allocation *alloc, u32 *block_len) 257 { 258 int len = min(*block_len, aux_info.blocks_per_tind); 259 260 int tind_block = get_oob_block(alloc, 0); 261 inode->i_block[EXT4_TIND_BLOCK] = tind_block; 262 263 if (advance_oob_blocks(alloc, 1)) { 264 error("failed to advance oob block"); 265 return -1; 266 } 267 268 u32 *tind_block_data = calloc(info.block_size, 1); 269 sparse_file_add_data(info.sparse_file, tind_block_data, info.block_size, 270 tind_block); 271 272 fill_tindirect_block(tind_block_data, len, alloc); 273 274 if (advance_blocks(alloc, len)) { 275 error("failed to advance %d blocks", len); 276 return -1; 277 } 278 279 *block_len -= len; 280 return 0; 281 } 282 283 static void reserve_all_indirect_blocks(struct block_allocation *alloc, u32 len) 284 { 285 if (len <= EXT4_NDIR_BLOCKS) 286 return; 287 288 len -= EXT4_NDIR_BLOCKS; 289 advance_blocks(alloc, EXT4_NDIR_BLOCKS); 290 291 u32 ind_block_len = min(aux_info.blocks_per_ind, len); 292 reserve_indirect_block(alloc, ind_block_len); 293 294 len -= ind_block_len; 295 if (len == 0) 296 return; 297 298 u32 dind_block_len = min(aux_info.blocks_per_dind, len); 299 reserve_dindirect_block(alloc, dind_block_len); 300 301 len -= dind_block_len; 302 if (len == 0) 303 return; 304 305 u32 tind_block_len = min(aux_info.blocks_per_tind, len); 306 reserve_tindirect_block(alloc, tind_block_len); 307 308 len -= tind_block_len; 309 if (len == 0) 310 return; 311 312 error("%d blocks remaining", len); 313 } 314 315 static u32 indirect_blocks_needed(u32 len) 316 { 317 u32 ind = 0; 318 319 if (len <= EXT4_NDIR_BLOCKS) 320 return ind; 321 322 len -= EXT4_NDIR_BLOCKS; 323 324 /* We will need an indirect block for the rest of the blocks */ 325 ind += DIV_ROUND_UP(len, aux_info.blocks_per_ind); 326 327 if (len <= aux_info.blocks_per_ind) 328 return ind; 329 330 len -= aux_info.blocks_per_ind; 331 332 ind += DIV_ROUND_UP(len, aux_info.blocks_per_dind); 333 334 if (len <= aux_info.blocks_per_dind) 335 return ind; 336 337 len -= aux_info.blocks_per_dind; 338 339 ind += DIV_ROUND_UP(len, aux_info.blocks_per_tind); 340 341 if (len <= aux_info.blocks_per_tind) 342 return ind; 343 344 critical_error("request too large"); 345 return 0; 346 } 347 348 static int do_inode_attach_indirect(struct ext4_inode *inode, 349 struct block_allocation *alloc, u32 block_len) 350 { 351 u32 count = block_len; 352 353 if (inode_attach_direct_blocks(inode, alloc, &count)) { 354 error("failed to attach direct blocks to inode"); 355 return -1; 356 } 357 358 if (count > 0) { 359 if (inode_attach_indirect_blocks(inode, alloc, &count)) { 360 error("failed to attach indirect blocks to inode"); 361 return -1; 362 } 363 } 364 365 if (count > 0) { 366 if (inode_attach_dindirect_blocks(inode, alloc, &count)) { 367 error("failed to attach dindirect blocks to inode"); 368 return -1; 369 } 370 } 371 372 if (count > 0) { 373 if (inode_attach_tindirect_blocks(inode, alloc, &count)) { 374 error("failed to attach tindirect blocks to inode"); 375 return -1; 376 } 377 } 378 379 if (count) { 380 error("blocks left after triply-indirect allocation"); 381 return -1; 382 } 383 384 rewind_alloc(alloc); 385 386 return 0; 387 } 388 389 static struct block_allocation *do_inode_allocate_indirect( 390 u32 block_len) 391 { 392 u32 indirect_len = indirect_blocks_needed(block_len); 393 394 struct block_allocation *alloc = allocate_blocks(block_len + indirect_len); 395 396 if (alloc == NULL) { 397 error("Failed to allocate %d blocks", block_len + indirect_len); 398 return NULL; 399 } 400 401 return alloc; 402 } 403 404 /* Allocates enough blocks to hold len bytes and connects them to an inode */ 405 void inode_allocate_indirect(struct ext4_inode *inode, unsigned long len) 406 { 407 struct block_allocation *alloc; 408 u32 block_len = DIV_ROUND_UP(len, info.block_size); 409 u32 indirect_len = indirect_blocks_needed(block_len); 410 411 alloc = do_inode_allocate_indirect(block_len); 412 if (alloc == NULL) { 413 error("failed to allocate extents for %lu bytes", len); 414 return; 415 } 416 417 reserve_all_indirect_blocks(alloc, block_len); 418 rewind_alloc(alloc); 419 420 if (do_inode_attach_indirect(inode, alloc, block_len)) 421 error("failed to attach blocks to indirect inode"); 422 423 inode->i_flags = 0; 424 inode->i_blocks_lo = (block_len + indirect_len) * info.block_size / 512; 425 inode->i_size_lo = len; 426 427 free_alloc(alloc); 428 } 429 430 void inode_attach_resize(struct ext4_inode *inode, 431 struct block_allocation *alloc) 432 { 433 u32 block_len = block_allocation_len(alloc); 434 u32 superblocks = block_len / info.bg_desc_reserve_blocks; 435 u32 i, j; 436 u64 blocks; 437 u64 size; 438 439 if (block_len % info.bg_desc_reserve_blocks) 440 critical_error("reserved blocks not a multiple of %d", 441 info.bg_desc_reserve_blocks); 442 443 append_oob_allocation(alloc, 1); 444 u32 dind_block = get_oob_block(alloc, 0); 445 446 u32 *dind_block_data = calloc(info.block_size, 1); 447 if (!dind_block_data) 448 critical_error_errno("calloc"); 449 sparse_file_add_data(info.sparse_file, dind_block_data, info.block_size, 450 dind_block); 451 452 u32 *ind_block_data = calloc(info.block_size, info.bg_desc_reserve_blocks); 453 if (!ind_block_data) 454 critical_error_errno("calloc"); 455 sparse_file_add_data(info.sparse_file, ind_block_data, 456 info.block_size * info.bg_desc_reserve_blocks, 457 get_block(alloc, 0)); 458 459 for (i = 0; i < info.bg_desc_reserve_blocks; i++) { 460 int r = (i - aux_info.bg_desc_blocks) % info.bg_desc_reserve_blocks; 461 if (r < 0) 462 r += info.bg_desc_reserve_blocks; 463 464 dind_block_data[i] = get_block(alloc, r); 465 466 for (j = 1; j < superblocks; j++) { 467 u32 b = j * info.bg_desc_reserve_blocks + r; 468 ind_block_data[r * aux_info.blocks_per_ind + j - 1] = get_block(alloc, b); 469 } 470 } 471 472 u32 last_block = EXT4_NDIR_BLOCKS + aux_info.blocks_per_ind + 473 aux_info.blocks_per_ind * (info.bg_desc_reserve_blocks - 1) + 474 superblocks - 2; 475 476 blocks = ((u64)block_len + 1) * info.block_size / 512; 477 size = (u64)last_block * info.block_size; 478 479 inode->i_block[EXT4_DIND_BLOCK] = dind_block; 480 inode->i_flags = 0; 481 inode->i_blocks_lo = blocks; 482 inode->osd2.linux2.l_i_blocks_high = blocks >> 32; 483 inode->i_size_lo = size; 484 inode->i_size_high = size >> 32; 485 } 486 487 /* Allocates enough blocks to hold len bytes, with backing_len bytes in a data 488 buffer, and connects them to an inode. Returns a pointer to the data 489 buffer. */ 490 u8 *inode_allocate_data_indirect(struct ext4_inode *inode, unsigned long len, 491 unsigned long backing_len) 492 { 493 struct block_allocation *alloc; 494 u32 block_len = DIV_ROUND_UP(len, info.block_size); 495 u8 *data = NULL; 496 497 alloc = do_inode_allocate_indirect(block_len); 498 if (alloc == NULL) { 499 error("failed to allocate extents for %lu bytes", len); 500 return NULL; 501 } 502 503 if (backing_len) { 504 data = create_backing(alloc, backing_len); 505 if (!data) 506 error("failed to create backing for %lu bytes", backing_len); 507 } 508 509 rewind_alloc(alloc); 510 if (do_inode_attach_indirect(inode, alloc, block_len)) 511 error("failed to attach blocks to indirect inode"); 512 513 free_alloc(alloc); 514 515 return data; 516 } 517