2 * Copyright (c) 2012 Frantisek Princ
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup libext4
33 * @file libext4_balloc.c
34 * @brief Physical block allocator.
38 #include <sys/types.h>
41 /** Compute number of block group from block address.
43 * @param sb Superblock pointer.
44 * @param block_addr Absolute address of block.
46 * @return Block group index
49 static uint32_t ext4_balloc_get_bgid_of_block(ext4_superblock_t
*sb
,
52 uint32_t blocks_per_group
=
53 ext4_superblock_get_blocks_per_group(sb
);
54 uint32_t first_block
=
55 ext4_superblock_get_first_data_block(sb
);
57 /* First block == 0 or 1 */
59 return block_addr
/ blocks_per_group
;
61 return (block_addr
- 1) / blocks_per_group
;
66 * @param inode_ref Inode, where the block is allocated
67 * @param block_addr Absolute block address to free
72 int ext4_balloc_free_block(ext4_inode_ref_t
*inode_ref
, uint32_t block_addr
)
74 ext4_filesystem_t
*fs
= inode_ref
->fs
;
75 ext4_superblock_t
*sb
= fs
->superblock
;
78 uint32_t block_group
= ext4_balloc_get_bgid_of_block(sb
, block_addr
);
79 uint32_t index_in_group
=
80 ext4_filesystem_blockaddr2_index_in_group(sb
, block_addr
);
82 /* Load block group reference */
83 ext4_block_group_ref_t
*bg_ref
;
84 int rc
= ext4_filesystem_get_block_group_ref(fs
, block_group
, &bg_ref
);
88 /* Load block with bitmap */
89 uint32_t bitmap_block_addr
=
90 ext4_block_group_get_block_bitmap(bg_ref
->block_group
, sb
);
91 block_t
*bitmap_block
;
92 rc
= block_get(&bitmap_block
, fs
->device
, bitmap_block_addr
, 0);
94 ext4_filesystem_put_block_group_ref(bg_ref
);
99 ext4_bitmap_free_bit(bitmap_block
->data
, index_in_group
);
100 bitmap_block
->dirty
= true;
102 /* Release block with bitmap */
103 rc
= block_put(bitmap_block
);
105 /* Error in saving bitmap */
106 ext4_filesystem_put_block_group_ref(bg_ref
);
110 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
112 /* Update superblock free blocks count */
113 uint32_t sb_free_blocks
=
114 ext4_superblock_get_free_blocks_count(sb
);
116 ext4_superblock_set_free_blocks_count(sb
, sb_free_blocks
);
118 /* Update inode blocks count */
119 uint64_t ino_blocks
=
120 ext4_inode_get_blocks_count(sb
, inode_ref
->inode
);
121 ino_blocks
-= block_size
/ EXT4_INODE_BLOCK_SIZE
;
122 ext4_inode_set_blocks_count(sb
, inode_ref
->inode
, ino_blocks
);
123 inode_ref
->dirty
= true;
125 /* Update block group free blocks count */
126 uint32_t free_blocks
=
127 ext4_block_group_get_free_blocks_count(bg_ref
->block_group
, sb
);
129 ext4_block_group_set_free_blocks_count(bg_ref
->block_group
,
131 bg_ref
->dirty
= true;
133 /* Release block group reference */
134 return ext4_filesystem_put_block_group_ref(bg_ref
);
137 /** Free continuous set of blocks.
139 * @param inode_ref Inode, where the blocks are allocated
140 * @param first First block to release
141 * @param count Number of blocks to release
144 int ext4_balloc_free_blocks(ext4_inode_ref_t
*inode_ref
,
145 uint32_t first
, uint32_t count
)
147 ext4_filesystem_t
*fs
= inode_ref
->fs
;
148 ext4_superblock_t
*sb
= fs
->superblock
;
150 /* Compute indexes */
151 uint32_t block_group_first
=
152 ext4_balloc_get_bgid_of_block(sb
, first
);
153 uint32_t block_group_last
=
154 ext4_balloc_get_bgid_of_block(sb
, first
+ count
- 1);
156 assert(block_group_first
== block_group_last
);
158 /* Load block group reference */
159 ext4_block_group_ref_t
*bg_ref
;
160 int rc
= ext4_filesystem_get_block_group_ref(fs
, block_group_first
, &bg_ref
);
164 uint32_t index_in_group_first
=
165 ext4_filesystem_blockaddr2_index_in_group(sb
, first
);
167 /* Load block with bitmap */
168 uint32_t bitmap_block_addr
=
169 ext4_block_group_get_block_bitmap(bg_ref
->block_group
, sb
);
171 block_t
*bitmap_block
;
172 rc
= block_get(&bitmap_block
, fs
->device
, bitmap_block_addr
, 0);
174 ext4_filesystem_put_block_group_ref(bg_ref
);
179 ext4_bitmap_free_bits(bitmap_block
->data
, index_in_group_first
, count
);
180 bitmap_block
->dirty
= true;
182 /* Release block with bitmap */
183 rc
= block_put(bitmap_block
);
185 /* Error in saving bitmap */
186 ext4_filesystem_put_block_group_ref(bg_ref
);
190 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
192 /* Update superblock free blocks count */
193 uint32_t sb_free_blocks
=
194 ext4_superblock_get_free_blocks_count(sb
);
195 sb_free_blocks
+= count
;
196 ext4_superblock_set_free_blocks_count(sb
, sb_free_blocks
);
198 /* Update inode blocks count */
199 uint64_t ino_blocks
=
200 ext4_inode_get_blocks_count(sb
, inode_ref
->inode
);
201 ino_blocks
-= count
* (block_size
/ EXT4_INODE_BLOCK_SIZE
);
202 ext4_inode_set_blocks_count(sb
, inode_ref
->inode
, ino_blocks
);
203 inode_ref
->dirty
= true;
205 /* Update block group free blocks count */
206 uint32_t free_blocks
=
207 ext4_block_group_get_free_blocks_count(bg_ref
->block_group
, sb
);
208 free_blocks
+= count
;
209 ext4_block_group_set_free_blocks_count(bg_ref
->block_group
,
211 bg_ref
->dirty
= true;
213 /* Release block group reference */
214 return ext4_filesystem_put_block_group_ref(bg_ref
);
217 /** Compute first block for data in block group.
219 * @param sb Pointer to superblock
220 * @param bg Pointer to block group
221 * @param bgid Index of block group
223 * @return Absolute block index of first block
226 uint32_t ext4_balloc_get_first_data_block_in_group(ext4_superblock_t
*sb
,
227 ext4_block_group_ref_t
*bg_ref
)
229 uint32_t block_group_count
= ext4_superblock_get_block_group_count(sb
);
230 uint32_t inode_table_first_block
=
231 ext4_block_group_get_inode_table_first_block(bg_ref
->block_group
, sb
);
232 uint16_t inode_table_item_size
= ext4_superblock_get_inode_size(sb
);
233 uint32_t inodes_per_group
= ext4_superblock_get_inodes_per_group(sb
);
234 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
235 uint32_t inode_table_bytes
;
237 if (bg_ref
->index
< block_group_count
- 1) {
238 inode_table_bytes
= inodes_per_group
* inode_table_item_size
;
240 /* Last block group could be smaller */
241 uint32_t inodes_count_total
= ext4_superblock_get_inodes_count(sb
);
243 (inodes_count_total
- ((block_group_count
- 1) * inodes_per_group
)) *
244 inode_table_item_size
;
247 uint32_t inode_table_blocks
= inode_table_bytes
/ block_size
;
249 if (inode_table_bytes
% block_size
)
250 inode_table_blocks
++;
252 return inode_table_first_block
+ inode_table_blocks
;
255 /** Compute 'goal' for allocation algorithm.
257 * @param inode_ref Reference to inode, to allocate block for
259 * @return Goal block number
262 static int ext4_balloc_find_goal(ext4_inode_ref_t
*inode_ref
, uint32_t *goal
)
265 ext4_superblock_t
*sb
= inode_ref
->fs
->superblock
;
267 uint64_t inode_size
= ext4_inode_get_size(sb
, inode_ref
->inode
);
268 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
269 uint32_t inode_block_count
= inode_size
/ block_size
;
271 if (inode_size
% block_size
!= 0)
274 /* If inode has some blocks, get last block address + 1 */
275 if (inode_block_count
> 0) {
276 int rc
= ext4_filesystem_get_inode_data_block_index(inode_ref
,
277 inode_block_count
- 1, goal
);
286 /* If goal == 0, sparse file -> continue */
289 /* Identify block group of inode */
290 uint32_t inodes_per_group
= ext4_superblock_get_inodes_per_group(sb
);
291 uint32_t block_group
= (inode_ref
->index
- 1) / inodes_per_group
;
292 block_size
= ext4_superblock_get_block_size(sb
);
294 /* Load block group reference */
295 ext4_block_group_ref_t
*bg_ref
;
296 int rc
= ext4_filesystem_get_block_group_ref(inode_ref
->fs
,
297 block_group
, &bg_ref
);
301 /* Compute indexes */
302 uint32_t block_group_count
= ext4_superblock_get_block_group_count(sb
);
303 uint32_t inode_table_first_block
=
304 ext4_block_group_get_inode_table_first_block(bg_ref
->block_group
, sb
);
305 uint16_t inode_table_item_size
= ext4_superblock_get_inode_size(sb
);
306 uint32_t inode_table_bytes
;
308 /* Check for last block group */
309 if (block_group
< block_group_count
- 1) {
310 inode_table_bytes
= inodes_per_group
* inode_table_item_size
;
312 /* Last block group could be smaller */
313 uint32_t inodes_count_total
= ext4_superblock_get_inodes_count(sb
);
315 (inodes_count_total
- ((block_group_count
- 1) * inodes_per_group
)) *
316 inode_table_item_size
;
319 uint32_t inode_table_blocks
= inode_table_bytes
/ block_size
;
321 if (inode_table_bytes
% block_size
)
322 inode_table_blocks
++;
324 *goal
= inode_table_first_block
+ inode_table_blocks
;
326 return ext4_filesystem_put_block_group_ref(bg_ref
);
329 /** Data block allocation algorithm.
331 * @param inode_ref Inode to allocate block for
332 * @param fblock Allocated block address
337 int ext4_balloc_alloc_block(ext4_inode_ref_t
*inode_ref
, uint32_t *fblock
)
339 uint32_t allocated_block
= 0;
341 uint32_t bitmap_block_addr
;
342 block_t
*bitmap_block
;
343 uint32_t rel_block_idx
= 0;
347 int rc
= ext4_balloc_find_goal(inode_ref
, &goal
);
350 else if (goal
== 0) {
351 /* no goal found => partition is full */
355 ext4_superblock_t
*sb
= inode_ref
->fs
->superblock
;
357 /* Load block group number for goal and relative index */
358 uint32_t block_group
= ext4_balloc_get_bgid_of_block(sb
, goal
);
359 uint32_t index_in_group
=
360 ext4_filesystem_blockaddr2_index_in_group(sb
, goal
);
362 /* Load block group reference */
363 ext4_block_group_ref_t
*bg_ref
;
364 rc
= ext4_filesystem_get_block_group_ref(inode_ref
->fs
,
365 block_group
, &bg_ref
);
369 /* Compute indexes */
370 uint32_t first_in_group
=
371 ext4_balloc_get_first_data_block_in_group(sb
, bg_ref
);
373 uint32_t first_in_group_index
=
374 ext4_filesystem_blockaddr2_index_in_group(sb
, first_in_group
);
376 if (index_in_group
< first_in_group_index
)
377 index_in_group
= first_in_group_index
;
379 /* Load block with bitmap */
381 ext4_block_group_get_block_bitmap(bg_ref
->block_group
, sb
);
383 rc
= block_get(&bitmap_block
, inode_ref
->fs
->device
,
384 bitmap_block_addr
, BLOCK_FLAGS_NONE
);
386 ext4_filesystem_put_block_group_ref(bg_ref
);
390 /* Check if goal is free */
391 if (ext4_bitmap_is_free_bit(bitmap_block
->data
, index_in_group
)) {
392 ext4_bitmap_set_bit(bitmap_block
->data
, index_in_group
);
393 bitmap_block
->dirty
= true;
394 rc
= block_put(bitmap_block
);
396 ext4_filesystem_put_block_group_ref(bg_ref
);
401 ext4_filesystem_index_in_group2blockaddr(sb
, index_in_group
,
407 uint32_t blocks_in_group
=
408 ext4_superblock_get_blocks_in_group(sb
, block_group
);
410 uint32_t end_idx
= (index_in_group
+ 63) & ~63;
411 if (end_idx
> blocks_in_group
)
412 end_idx
= blocks_in_group
;
414 /* Try to find free block near to goal */
415 for (uint32_t tmp_idx
= index_in_group
+ 1; tmp_idx
< end_idx
;
417 if (ext4_bitmap_is_free_bit(bitmap_block
->data
, tmp_idx
)) {
418 ext4_bitmap_set_bit(bitmap_block
->data
, tmp_idx
);
419 bitmap_block
->dirty
= true;
420 rc
= block_put(bitmap_block
);
425 ext4_filesystem_index_in_group2blockaddr(sb
, tmp_idx
,
432 /* Find free BYTE in bitmap */
433 rc
= ext4_bitmap_find_free_byte_and_set_bit(bitmap_block
->data
,
434 index_in_group
, &rel_block_idx
, blocks_in_group
);
436 bitmap_block
->dirty
= true;
437 rc
= block_put(bitmap_block
);
442 ext4_filesystem_index_in_group2blockaddr(sb
, rel_block_idx
,
448 /* Find free bit in bitmap */
449 rc
= ext4_bitmap_find_free_bit_and_set(bitmap_block
->data
,
450 index_in_group
, &rel_block_idx
, blocks_in_group
);
452 bitmap_block
->dirty
= true;
453 rc
= block_put(bitmap_block
);
458 ext4_filesystem_index_in_group2blockaddr(sb
, rel_block_idx
,
464 /* No free block found yet */
465 rc
= block_put(bitmap_block
);
467 ext4_filesystem_put_block_group_ref(bg_ref
);
471 rc
= ext4_filesystem_put_block_group_ref(bg_ref
);
475 /* Try other block groups */
476 uint32_t block_group_count
= ext4_superblock_get_block_group_count(sb
);
478 uint32_t bgid
= (block_group
+ 1) % block_group_count
;
479 uint32_t count
= block_group_count
;
482 rc
= ext4_filesystem_get_block_group_ref(inode_ref
->fs
, bgid
,
487 /* Load block with bitmap */
489 ext4_block_group_get_block_bitmap(bg_ref
->block_group
, sb
);
491 rc
= block_get(&bitmap_block
, inode_ref
->fs
->device
,
492 bitmap_block_addr
, 0);
494 ext4_filesystem_put_block_group_ref(bg_ref
);
498 /* Compute indexes */
500 ext4_balloc_get_first_data_block_in_group(sb
, bg_ref
);
502 ext4_filesystem_blockaddr2_index_in_group(sb
, first_in_group
);
503 blocks_in_group
= ext4_superblock_get_blocks_in_group(sb
, bgid
);
505 first_in_group_index
=
506 ext4_filesystem_blockaddr2_index_in_group(sb
, first_in_group
);
508 if (index_in_group
< first_in_group_index
)
509 index_in_group
= first_in_group_index
;
511 /* Try to find free byte in bitmap */
512 rc
= ext4_bitmap_find_free_byte_and_set_bit(bitmap_block
->data
,
513 index_in_group
, &rel_block_idx
, blocks_in_group
);
515 bitmap_block
->dirty
= true;
516 rc
= block_put(bitmap_block
);
518 ext4_filesystem_put_block_group_ref(bg_ref
);
523 ext4_filesystem_index_in_group2blockaddr(sb
, rel_block_idx
,
529 /* Try to find free bit in bitmap */
530 rc
= ext4_bitmap_find_free_bit_and_set(bitmap_block
->data
,
531 index_in_group
, &rel_block_idx
, blocks_in_group
);
533 bitmap_block
->dirty
= true;
534 rc
= block_put(bitmap_block
);
536 ext4_filesystem_put_block_group_ref(bg_ref
);
541 ext4_filesystem_index_in_group2blockaddr(sb
, rel_block_idx
,
547 rc
= block_put(bitmap_block
);
549 ext4_filesystem_put_block_group_ref(bg_ref
);
553 rc
= ext4_filesystem_put_block_group_ref(bg_ref
);
557 /* Goto next group */
558 bgid
= (bgid
+ 1) % block_group_count
;
565 /* Empty command - because of syntax */
568 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
570 /* Update superblock free blocks count */
571 uint32_t sb_free_blocks
= ext4_superblock_get_free_blocks_count(sb
);
573 ext4_superblock_set_free_blocks_count(sb
, sb_free_blocks
);
575 /* Update inode blocks (different block size!) count */
576 uint64_t ino_blocks
=
577 ext4_inode_get_blocks_count(sb
, inode_ref
->inode
);
578 ino_blocks
+= block_size
/ EXT4_INODE_BLOCK_SIZE
;
579 ext4_inode_set_blocks_count(sb
, inode_ref
->inode
, ino_blocks
);
580 inode_ref
->dirty
= true;
582 /* Update block group free blocks count */
583 uint32_t bg_free_blocks
=
584 ext4_block_group_get_free_blocks_count(bg_ref
->block_group
, sb
);
586 ext4_block_group_set_free_blocks_count(bg_ref
->block_group
, sb
,
588 bg_ref
->dirty
= true;
590 rc
= ext4_filesystem_put_block_group_ref(bg_ref
);
592 *fblock
= allocated_block
;
596 /** Try to allocate concrete block.
598 * @param inode_ref Inode to allocate block for
599 * @param fblock Block address to allocate
600 * @param free Output value - if target block is free
605 int ext4_balloc_try_alloc_block(ext4_inode_ref_t
*inode_ref
, uint32_t fblock
,
610 ext4_filesystem_t
*fs
= inode_ref
->fs
;
611 ext4_superblock_t
*sb
= fs
->superblock
;
613 /* Compute indexes */
614 uint32_t block_group
= ext4_balloc_get_bgid_of_block(sb
, fblock
);
615 uint32_t index_in_group
=
616 ext4_filesystem_blockaddr2_index_in_group(sb
, fblock
);
618 /* Load block group reference */
619 ext4_block_group_ref_t
*bg_ref
;
620 rc
= ext4_filesystem_get_block_group_ref(fs
, block_group
, &bg_ref
);
624 /* Load block with bitmap */
625 uint32_t bitmap_block_addr
=
626 ext4_block_group_get_block_bitmap(bg_ref
->block_group
, sb
);
627 block_t
*bitmap_block
;
628 rc
= block_get(&bitmap_block
, fs
->device
, bitmap_block_addr
, 0);
630 ext4_filesystem_put_block_group_ref(bg_ref
);
634 /* Check if block is free */
635 *free
= ext4_bitmap_is_free_bit(bitmap_block
->data
, index_in_group
);
637 /* Allocate block if possible */
639 ext4_bitmap_set_bit(bitmap_block
->data
, index_in_group
);
640 bitmap_block
->dirty
= true;
643 /* Release block with bitmap */
644 rc
= block_put(bitmap_block
);
646 /* Error in saving bitmap */
647 ext4_filesystem_put_block_group_ref(bg_ref
);
651 /* If block is not free, return */
655 uint32_t block_size
= ext4_superblock_get_block_size(sb
);
657 /* Update superblock free blocks count */
658 uint32_t sb_free_blocks
= ext4_superblock_get_free_blocks_count(sb
);
660 ext4_superblock_set_free_blocks_count(sb
, sb_free_blocks
);
662 /* Update inode blocks count */
663 uint64_t ino_blocks
=
664 ext4_inode_get_blocks_count(sb
, inode_ref
->inode
);
665 ino_blocks
+= block_size
/ EXT4_INODE_BLOCK_SIZE
;
666 ext4_inode_set_blocks_count(sb
, inode_ref
->inode
, ino_blocks
);
667 inode_ref
->dirty
= true;
669 /* Update block group free blocks count */
670 uint32_t free_blocks
=
671 ext4_block_group_get_free_blocks_count(bg_ref
->block_group
, sb
);
673 ext4_block_group_set_free_blocks_count(bg_ref
->block_group
,
675 bg_ref
->dirty
= true;
678 return ext4_filesystem_put_block_group_ref(bg_ref
);