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