2 * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
\r
6 * Copyright (c) 2012 Martin Sucha
\r
7 * Copyright (c) 2012 Frantisek Princ
\r
8 * All rights reserved.
\r
10 * Redistribution and use in source and binary forms, with or without
\r
11 * modification, are permitted provided that the following conditions
\r
14 * - Redistributions of source code must retain the above copyright
\r
15 * notice, this list of conditions and the following disclaimer.
\r
16 * - Redistributions in binary form must reproduce the above copyright
\r
17 * notice, this list of conditions and the following disclaimer in the
\r
18 * documentation and/or other materials provided with the distribution.
\r
19 * - The name of the author may not be used to endorse or promote products
\r
20 * derived from this software without specific prior written permission.
\r
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
\r
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
\r
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
\r
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
\r
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
\r
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
\r
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
\r
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
\r
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
34 /** @addtogroup lwext4
\r
38 * @file ext4_extent.c
\r
39 * @brief More complex filesystem functions.
\r
42 #include <ext4_config.h>
\r
43 #include <ext4_extent.h>
\r
44 #include <ext4_inode.h>
\r
45 #include <ext4_super.h>
\r
46 #include <ext4_blockdev.h>
\r
47 #include <ext4_balloc.h>
\r
52 uint32_t ext4_extent_get_first_block(struct ext4_extent *extent)
\r
54 return to_le32(extent->first_block);
\r
58 void ext4_extent_set_first_block(struct ext4_extent *extent, uint32_t iblock)
\r
60 extent->first_block = to_le32(iblock);
\r
64 uint16_t ext4_extent_get_block_count(struct ext4_extent *extent)
\r
66 return to_le16(extent->block_count);
\r
70 void ext4_extent_set_block_count(struct ext4_extent *extent, uint16_t count)
\r
72 extent->block_count = to_le16(count);
\r
76 uint64_t ext4_extent_get_start(struct ext4_extent *extent)
\r
78 return ((uint64_t)to_le16(extent->start_hi)) << 32 |
\r
79 ((uint64_t)to_le32(extent->start_lo));
\r
83 void ext4_extent_set_start(struct ext4_extent *extent, uint64_t fblock)
\r
85 extent->start_lo = to_le32((fblock << 32) >> 32);
\r
86 extent->start_hi = to_le16((uint16_t)(fblock >> 32));
\r
90 uint32_t ext4_extent_index_get_first_block(struct ext4_extent_index *index)
\r
92 return to_le32(index->first_block);
\r
96 void ext4_extent_index_set_first_block(struct ext4_extent_index *index,
\r
99 index->first_block = to_le32(iblock);
\r
103 uint64_t ext4_extent_index_get_leaf(struct ext4_extent_index *index)
\r
105 return ((uint64_t) to_le16(index->leaf_hi)) << 32 |
\r
106 ((uint64_t)to_le32(index->leaf_lo));
\r
109 void ext4_extent_index_set_leaf(struct ext4_extent_index *index,
\r
112 index->leaf_lo = to_le32((fblock << 32) >> 32);
\r
113 index->leaf_hi = to_le16((uint16_t) (fblock >> 32));
\r
117 uint16_t ext4_extent_header_get_magic(struct ext4_extent_header *header)
\r
119 return to_le16(header->magic);
\r
123 void ext4_extent_header_set_magic(struct ext4_extent_header *header,
\r
126 header->magic = to_le16(magic);
\r
130 uint16_t ext4_extent_header_get_entries_count(struct ext4_extent_header *header)
\r
132 return to_le16(header->entries_count);
\r
136 void ext4_extent_header_set_entries_count(struct ext4_extent_header *header,
\r
139 header->entries_count = to_le16(count);
\r
143 uint16_t ext4_extent_header_get_max_entries_count(struct ext4_extent_header *header)
\r
145 return to_le16(header->max_entries_count);
\r
149 void ext4_extent_header_set_max_entries_count(struct ext4_extent_header *header,
\r
150 uint16_t max_count)
\r
152 header->max_entries_count = to_le16(max_count);
\r
156 uint16_t ext4_extent_header_get_depth(struct ext4_extent_header *header)
\r
158 return to_le16(header->depth);
\r
162 void ext4_extent_header_set_depth(struct ext4_extent_header *header,
\r
165 header->depth = to_le16(depth);
\r
169 uint32_t ext4_extent_header_get_generation(struct ext4_extent_header *header)
\r
171 return to_le32(header->generation);
\r
175 void ext4_extent_header_set_generation(struct ext4_extent_header *header,
\r
176 uint32_t generation)
\r
178 header->generation = to_le32(generation);
\r
181 /**@brief Binary search in extent index node.
\r
182 * @param header Extent header of index node
\r
183 * @param index Output value - found index will be set here
\r
184 * @param iblock Logical block number to find in index node */
\r
185 static void ext4_extent_binsearch_idx(struct ext4_extent_header *header,
\r
186 struct ext4_extent_index **index, uint32_t iblock)
\r
188 struct ext4_extent_index *r;
\r
189 struct ext4_extent_index *l;
\r
190 struct ext4_extent_index *m;
\r
192 uint16_t entries_count =
\r
193 ext4_extent_header_get_entries_count(header);
\r
195 /* Initialize bounds */
\r
196 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
\r
197 r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1;
\r
199 /* Do binary search */
\r
201 m = l + (r - l) / 2;
\r
202 uint32_t first_block = ext4_extent_index_get_first_block(m);
\r
204 if (iblock < first_block)
\r
210 /* Set output value */
\r
214 /**@brief Binary search in extent leaf node.
\r
215 * @param header Extent header of leaf node
\r
216 * @param extent Output value - found extent will be set here,
\r
217 * or NULL if node is empty
\r
218 * @param iblock Logical block number to find in leaf node */
\r
219 static void ext4_extent_binsearch(struct ext4_extent_header *header,
\r
220 struct ext4_extent **extent, uint32_t iblock)
\r
222 struct ext4_extent *r;
\r
223 struct ext4_extent *l;
\r
224 struct ext4_extent *m;
\r
226 uint16_t entries_count =
\r
227 ext4_extent_header_get_entries_count(header);
\r
229 if (entries_count == 0) {
\r
230 /* this leaf is empty */
\r
235 /* Initialize bounds */
\r
236 l = EXT4_EXTENT_FIRST(header) + 1;
\r
237 r = EXT4_EXTENT_FIRST(header) + entries_count - 1;
\r
239 /* Do binary search */
\r
241 m = l + (r - l) / 2;
\r
242 uint32_t first_block = ext4_extent_get_first_block(m);
\r
244 if (iblock < first_block)
\r
250 /* Set output value */
\r
255 int ext4_extent_find_block(struct ext4_inode_ref *inode_ref, uint32_t iblock,
\r
259 /* Compute bound defined by i-node size */
\r
260 uint64_t inode_size =
\r
261 ext4_inode_get_size(&inode_ref->fs->sb, inode_ref->inode);
\r
263 uint32_t block_size =
\r
264 ext4_sb_get_block_size(&inode_ref->fs->sb);
\r
266 uint32_t last_idx = (inode_size - 1) / block_size;
\r
268 /* Check if requested iblock is not over size of i-node */
\r
269 if (iblock > last_idx) {
\r
274 struct ext4_block block;
\r
277 /* Walk through extent tree */
\r
278 struct ext4_extent_header *header =
\r
279 ext4_inode_get_extent_header(inode_ref->inode);
\r
281 while (ext4_extent_header_get_depth(header) != 0) {
\r
282 /* Search index in node */
\r
283 struct ext4_extent_index *index;
\r
284 ext4_extent_binsearch_idx(header, &index, iblock);
\r
286 /* Load child node and set values for the next iteration */
\r
287 uint64_t child = ext4_extent_index_get_leaf(index);
\r
290 rc = ext4_block_set(inode_ref->fs->bdev, &block);
\r
296 int rc = ext4_block_get(inode_ref->fs->bdev, &block, child);
\r
300 header = (struct ext4_extent_header *)block.data;
\r
303 /* Search extent in the leaf block */
\r
304 struct ext4_extent* extent = NULL;
\r
305 ext4_extent_binsearch(header, &extent, iblock);
\r
307 /* Prevent empty leaf */
\r
308 if (extent == NULL) {
\r
311 /* Compute requested physical block address */
\r
312 uint32_t phys_block;
\r
313 uint32_t first = ext4_extent_get_first_block(extent);
\r
314 phys_block = ext4_extent_get_start(extent) + iblock - first;
\r
316 *fblock = phys_block;
\r
321 rc = ext4_block_set(inode_ref->fs->bdev, &block);
\r
329 /**@brief Find extent for specified iblock.
\r
330 * This function is used for finding block in the extent tree with
\r
331 * saving the path through the tree for possible future modifications.
\r
332 * @param inode_ref I-node to read extent tree from
\r
333 * @param iblock Iblock to find extent for
\r
334 * @param ret_path Output value for loaded path from extent tree
\r
335 * @return Error code */
\r
336 static int ext4_extent_find_extent(struct ext4_inode_ref *inode_ref,
\r
337 uint32_t iblock, struct ext4_extent_path **ret_path)
\r
339 struct ext4_extent_header *eh =
\r
340 ext4_inode_get_extent_header(inode_ref->inode);
\r
342 uint16_t depth = ext4_extent_header_get_depth(eh);
\r
344 struct ext4_extent_path *tmp_path;
\r
346 /* Added 2 for possible tree growing */
\r
347 tmp_path = malloc(sizeof(struct ext4_extent_path) * (depth + 2));
\r
348 if (tmp_path == NULL)
\r
351 /* Initialize structure for algorithm start */
\r
352 tmp_path[0].block = inode_ref->block;
\r
353 tmp_path[0].header = eh;
\r
355 /* Walk through the extent tree */
\r
358 while (ext4_extent_header_get_depth(eh) != 0) {
\r
359 /* Search index in index node by iblock */
\r
360 ext4_extent_binsearch_idx(tmp_path[pos].header,
\r
361 &tmp_path[pos].index, iblock);
\r
363 tmp_path[pos].depth = depth;
\r
364 tmp_path[pos].extent = NULL;
\r
366 ext4_assert(tmp_path[pos].index != 0);
\r
368 /* Load information for the next iteration */
\r
369 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
\r
371 struct ext4_block block;
\r
372 rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);
\r
378 eh = (struct ext4_extent_header *)block.data;
\r
379 tmp_path[pos].block = block;
\r
380 tmp_path[pos].header = eh;
\r
383 tmp_path[pos].depth = 0;
\r
384 tmp_path[pos].extent = NULL;
\r
385 tmp_path[pos].index = NULL;
\r
387 /* Find extent in the leaf node */
\r
388 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
\r
389 *ret_path = tmp_path;
\r
395 * Put loaded blocks
\r
396 * From 1: 0 is a block with inode data
\r
398 for (i = 1; i < tmp_path->depth; ++i) {
\r
399 if (tmp_path[i].block.lb_id){
\r
400 int r = ext4_block_set(inode_ref->fs->bdev, &tmp_path[i].block);
\r
406 /* Destroy temporary data structure */
\r
412 /**@brief Release extent and all data blocks covered by the extent.
\r
413 * @param inode_ref I-node to release extent and block from
\r
414 * @param extent Extent to release
\r
415 * @return Error code */
\r
416 static int ext4_extent_release(struct ext4_inode_ref *inode_ref,
\r
417 struct ext4_extent *extent)
\r
419 /* Compute number of the first physical block to release */
\r
420 uint64_t start = ext4_extent_get_start(extent);
\r
421 uint16_t block_count = ext4_extent_get_block_count(extent);
\r
423 return ext4_balloc_free_blocks(inode_ref, start, block_count);
\r
426 /** Recursively release the whole branch of the extent tree.
\r
427 * For each entry of the node release the subbranch and finally release
\r
428 * the node. In the leaf node all extents will be released.
\r
429 * @param inode_ref I-node where the branch is released
\r
430 * @param index Index in the non-leaf node to be released
\r
431 * with the whole subtree
\r
432 * @return Error code */
\r
433 static int ext4_extent_release_branch(struct ext4_inode_ref *inode_ref,
\r
434 struct ext4_extent_index *index)
\r
436 uint32_t fblock = ext4_extent_index_get_leaf(index);
\r
438 struct ext4_block block;
\r
439 int rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);
\r
443 struct ext4_extent_header *header = (void *)block.data;
\r
445 if (ext4_extent_header_get_depth(header)) {
\r
446 /* The node is non-leaf, do recursion */
\r
447 struct ext4_extent_index *idx = EXT4_EXTENT_FIRST_INDEX(header);
\r
449 /* Release all subbranches */
\r
450 for (i = 0; i < ext4_extent_header_get_entries_count(header);
\r
452 rc = ext4_extent_release_branch(inode_ref, idx);
\r
457 /* Leaf node reached */
\r
458 struct ext4_extent *ext = EXT4_EXTENT_FIRST(header);
\r
460 /* Release all extents and stop recursion */
\r
461 for (i = 0; i < ext4_extent_header_get_entries_count(header);
\r
463 rc = ext4_extent_release(inode_ref, ext);
\r
469 /* Release data block where the node was stored */
\r
471 rc = ext4_block_set(inode_ref->fs->bdev, &block);
\r
475 return ext4_balloc_free_block(inode_ref, fblock);
\r
479 int ext4_extent_release_blocks_from(struct ext4_inode_ref *inode_ref,
\r
480 uint32_t iblock_from)
\r
482 /* Find the first extent to modify */
\r
483 struct ext4_extent_path *path;
\r
485 int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
\r
489 /* Jump to last item of the path (extent) */
\r
490 struct ext4_extent_path *path_ptr = path;
\r
491 while (path_ptr->depth != 0)
\r
494 ext4_assert(path_ptr->extent != NULL);
\r
496 /* First extent maybe released partially */
\r
497 uint32_t first_iblock =
\r
498 ext4_extent_get_first_block(path_ptr->extent);
\r
499 uint32_t first_fblock =
\r
500 ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock;
\r
502 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
\r
504 uint16_t delete_count = block_count -
\r
505 (ext4_extent_get_start(path_ptr->extent) - first_fblock);
\r
507 /* Release all blocks */
\r
508 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
\r
512 /* Correct counter */
\r
513 block_count -= delete_count;
\r
514 ext4_extent_set_block_count(path_ptr->extent, block_count);
\r
516 /* Initialize the following loop */
\r
518 ext4_extent_header_get_entries_count(path_ptr->header);
\r
519 struct ext4_extent *tmp_ext = path_ptr->extent + 1;
\r
520 struct ext4_extent *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
\r
522 /* If first extent empty, release it */
\r
523 if (block_count == 0)
\r
526 /* Release all successors of the first extent in the same node */
\r
527 while (tmp_ext < stop_ext) {
\r
528 first_fblock = ext4_extent_get_start(tmp_ext);
\r
529 delete_count = ext4_extent_get_block_count(tmp_ext);
\r
531 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
\r
539 ext4_extent_header_set_entries_count(path_ptr->header, entries);
\r
540 path_ptr->block.dirty = true;
\r
542 /* If leaf node is empty, parent entry must be modified */
\r
543 bool remove_parent_record = false;
\r
545 /* Don't release root block (including inode data) !!! */
\r
546 if ((path_ptr != path) && (entries == 0)) {
\r
547 rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id);
\r
551 remove_parent_record = true;
\r
554 /* Jump to the parent */
\r
557 /* Release all successors in all tree levels */
\r
558 while (path_ptr >= path) {
\r
559 entries = ext4_extent_header_get_entries_count(path_ptr->header);
\r
560 struct ext4_extent_index *index = path_ptr->index + 1;
\r
561 struct ext4_extent_index *stop =
\r
562 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
\r
564 /* Correct entries count because of changes in the previous iteration */
\r
565 if (remove_parent_record)
\r
568 /* Iterate over all entries and release the whole subtrees */
\r
569 while (index < stop) {
\r
570 rc = ext4_extent_release_branch(inode_ref, index);
\r
578 ext4_extent_header_set_entries_count(path_ptr->header, entries);
\r
579 path_ptr->block.dirty = true;
\r
581 /* Free the node if it is empty */
\r
582 if ((entries == 0) && (path_ptr != path)) {
\r
583 rc = ext4_balloc_free_block(inode_ref, path_ptr->block.lb_id);
\r
587 /* Mark parent to be checked */
\r
588 remove_parent_record = true;
\r
590 remove_parent_record = false;
\r
596 ext4_extent_header_set_depth(path->header, 0);
\r
600 * Put loaded blocks
\r
601 * starting from 1: 0 is a block with inode data
\r
603 for (i = 1; i <= path->depth; ++i) {
\r
604 if (path[i].block.lb_id){
\r
605 int r = ext4_block_set(inode_ref->fs->bdev, &path[i].block);
\r
611 /* Destroy temporary data structure */
\r
617 /**@brief Append new extent to the i-node and do some splitting if necessary.
\r
618 * @param inode_ref I-node to append extent to
\r
619 * @param path Path in the extent tree for possible splitting
\r
620 * @param last_path_item Input/output parameter for pointer to the last
\r
621 * valid item in the extent tree path
\r
622 * @param iblock Logical index of block to append extent for
\r
623 * @return Error code */
\r
624 static int ext4_extent_append_extent(struct ext4_inode_ref *inode_ref,
\r
625 struct ext4_extent_path *path, uint32_t iblock)
\r
627 struct ext4_extent_path *path_ptr = path + path->depth;
\r
629 uint32_t block_size =
\r
630 ext4_sb_get_block_size(&inode_ref->fs->sb);
\r
632 /* Start splitting */
\r
633 while (path_ptr > path) {
\r
635 ext4_extent_header_get_entries_count(path_ptr->header);
\r
637 ext4_extent_header_get_max_entries_count(path_ptr->header);
\r
639 if (entries == limit) {
\r
640 /* Full node - allocate block for new one */
\r
642 int rc = ext4_balloc_alloc_block(inode_ref, &fblock);
\r
646 struct ext4_block block;
\r
647 rc = ext4_block_get(inode_ref->fs->bdev, &block, fblock);
\r
649 ext4_balloc_free_block(inode_ref, fblock);
\r
653 /* Put back not modified old block */
\r
654 rc = ext4_block_set(inode_ref->fs->bdev, &path_ptr->block);
\r
656 ext4_balloc_free_block(inode_ref, fblock);
\r
660 /* Initialize newly allocated block and remember it */
\r
661 memset(block.data, 0, block_size);
\r
662 path_ptr->block = block;
\r
664 /* Update pointers in extent path structure */
\r
665 path_ptr->header = (void *)block.data;
\r
666 if (path_ptr->depth) {
\r
667 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
\r
668 ext4_extent_index_set_first_block(path_ptr->index, iblock);
\r
669 ext4_extent_index_set_leaf(path_ptr->index,
\r
670 (path_ptr + 1)->block.lb_id);
\r
671 limit = (block_size - sizeof(struct ext4_extent_header)) /
\r
672 sizeof(struct ext4_extent_index);
\r
674 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
\r
675 ext4_extent_set_first_block(path_ptr->extent, iblock);
\r
676 limit = (block_size - sizeof(struct ext4_extent_header)) /
\r
677 sizeof(struct ext4_extent);
\r
680 /* Initialize on-disk structure (header) */
\r
681 ext4_extent_header_set_entries_count(path_ptr->header, 1);
\r
682 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
\r
683 ext4_extent_header_set_magic(path_ptr->header, EXT4_EXTENT_MAGIC);
\r
684 ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth);
\r
685 ext4_extent_header_set_generation(path_ptr->header, 0);
\r
687 path_ptr->block.dirty = true;
\r
689 /* Jump to the preceeding item */
\r
692 /* Node with free space */
\r
693 if (path_ptr->depth) {
\r
694 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
\r
695 ext4_extent_index_set_first_block(path_ptr->index, iblock);
\r
696 ext4_extent_index_set_leaf(path_ptr->index,
\r
697 (path_ptr + 1)->block.lb_id);
\r
699 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
\r
700 ext4_extent_set_first_block(path_ptr->extent, iblock);
\r
703 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
\r
704 path_ptr->block.dirty = true;
\r
706 /* No more splitting needed */
\r
711 ext4_assert(path_ptr == path);
\r
713 /* Should be the root split too? */
\r
715 uint16_t entries = ext4_extent_header_get_entries_count(path->header);
\r
716 uint16_t limit = ext4_extent_header_get_max_entries_count(path->header);
\r
718 if (entries == limit) {
\r
719 uint32_t new_fblock;
\r
720 int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
\r
724 struct ext4_block block;
\r
725 rc = ext4_block_get(inode_ref->fs->bdev, &block, new_fblock);
\r
729 /* Initialize newly allocated block */
\r
730 memset(block.data, 0, block_size);
\r
732 /* Move data from root to the new block */
\r
733 memcpy(block.data, inode_ref->inode->blocks,
\r
734 EXT4_INODE_BLOCKS * sizeof(uint32_t));
\r
736 /* Data block is initialized */
\r
738 struct ext4_block *root_block = &path->block;
\r
739 uint16_t root_depth = path->depth;
\r
740 struct ext4_extent_header *root_header = path->header;
\r
742 /* Make space for tree growing */
\r
743 struct ext4_extent_path *new_root = path;
\r
744 struct ext4_extent_path *old_root = path + 1;
\r
746 size_t nbytes = sizeof(struct ext4_extent_path) * (path->depth + 1);
\r
747 memmove(old_root, new_root, nbytes);
\r
748 memset(new_root, 0, sizeof(struct ext4_extent_path));
\r
750 /* Update old root structure */
\r
751 old_root->block = block;
\r
752 old_root->header = (struct ext4_extent_header *)block.data;
\r
754 /* Add new entry and update limit for entries */
\r
755 if (old_root->depth) {
\r
756 limit = (block_size - sizeof(struct ext4_extent_header)) /
\r
757 sizeof(struct ext4_extent_index);
\r
758 old_root->index = EXT4_EXTENT_FIRST_INDEX(old_root->header) + entries;
\r
759 ext4_extent_index_set_first_block(old_root->index, iblock);
\r
760 ext4_extent_index_set_leaf(old_root->index,
\r
761 (old_root + 1)->block.lb_id);
\r
762 old_root->extent = NULL;
\r
764 limit = (block_size - sizeof(struct ext4_extent_header)) /
\r
765 sizeof(struct ext4_extent);
\r
766 old_root->extent = EXT4_EXTENT_FIRST(old_root->header) + entries;
\r
767 ext4_extent_set_first_block(old_root->extent, iblock);
\r
768 old_root->index = NULL;
\r
771 ext4_extent_header_set_entries_count(old_root->header, entries + 1);
\r
772 ext4_extent_header_set_max_entries_count(old_root->header, limit);
\r
774 old_root->block.dirty = true;
\r
776 /* Re-initialize new root metadata */
\r
777 new_root->depth = root_depth + 1;
\r
778 new_root->block = *root_block;
\r
779 new_root->header = root_header;
\r
780 new_root->extent = NULL;
\r
781 new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header);
\r
783 ext4_extent_header_set_depth(new_root->header, new_root->depth);
\r
785 /* Create new entry in root */
\r
786 ext4_extent_header_set_entries_count(new_root->header, 1);
\r
787 ext4_extent_index_set_first_block(new_root->index, 0);
\r
788 ext4_extent_index_set_leaf(new_root->index, new_fblock);
\r
790 new_root->block.dirty = true;
\r
793 path->index = EXT4_EXTENT_FIRST_INDEX(path->header) + entries;
\r
794 ext4_extent_index_set_first_block(path->index, iblock);
\r
795 ext4_extent_index_set_leaf(path->index, (path + 1)->block.lb_id);
\r
797 path->extent = EXT4_EXTENT_FIRST(path->header) + entries;
\r
798 ext4_extent_set_first_block(path->extent, iblock);
\r
801 ext4_extent_header_set_entries_count(path->header, entries + 1);
\r
802 path->block.dirty = true;
\r
809 int ext4_extent_append_block(struct ext4_inode_ref *inode_ref,
\r
810 uint32_t *iblock, uint32_t *fblock, bool update_size)
\r
813 struct ext4_sblock *sb = &inode_ref->fs->sb;
\r
814 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
\r
815 uint32_t block_size = ext4_sb_get_block_size(sb);
\r
817 /* Calculate number of new logical block */
\r
818 uint32_t new_block_idx = 0;
\r
819 if (inode_size > 0) {
\r
820 if ((inode_size % block_size) != 0)
\r
821 inode_size += block_size - (inode_size % block_size);
\r
823 new_block_idx = inode_size / block_size;
\r
826 /* Load the nearest leaf (with extent) */
\r
827 struct ext4_extent_path *path;
\r
828 int rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
\r
832 /* Jump to last item of the path (extent) */
\r
833 struct ext4_extent_path *path_ptr = path;
\r
834 while (path_ptr->depth != 0)
\r
837 /* Add new extent to the node if not present */
\r
838 if (path_ptr->extent == NULL)
\r
839 goto append_extent;
\r
841 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
\r
842 uint16_t block_limit = (1 << 15);
\r
844 uint32_t phys_block = 0;
\r
845 if (block_count < block_limit) {
\r
846 /* There is space for new block in the extent */
\r
847 if (block_count == 0) {
\r
848 /* Existing extent is empty */
\r
849 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
\r
853 /* Initialize extent */
\r
854 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
\r
855 ext4_extent_set_start(path_ptr->extent, phys_block);
\r
856 ext4_extent_set_block_count(path_ptr->extent, 1);
\r
858 /* Update i-node */
\r
860 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
\r
861 inode_ref->dirty = true;
\r
864 path_ptr->block.dirty = true;
\r
868 /* Existing extent contains some blocks */
\r
869 phys_block = ext4_extent_get_start(path_ptr->extent);
\r
870 phys_block += ext4_extent_get_block_count(path_ptr->extent);
\r
872 /* Check if the following block is free for allocation */
\r
874 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
\r
879 /* Target is not free, new block must be appended to new extent */
\r
880 goto append_extent;
\r
883 /* Update extent */
\r
884 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
\r
886 /* Update i-node */
\r
888 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
\r
889 inode_ref->dirty = true;
\r
892 path_ptr->block.dirty = true;
\r
900 /* Append new extent to the tree */
\r
903 /* Allocate new data block */
\r
904 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
\r
908 /* Append extent for new block (includes tree splitting if needed) */
\r
909 rc = ext4_extent_append_extent(inode_ref, path, new_block_idx);
\r
911 ext4_balloc_free_block(inode_ref, phys_block);
\r
915 uint32_t tree_depth = ext4_extent_header_get_depth(path->header);
\r
916 path_ptr = path + tree_depth;
\r
918 /* Initialize newly created extent */
\r
919 ext4_extent_set_block_count(path_ptr->extent, 1);
\r
920 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
\r
921 ext4_extent_set_start(path_ptr->extent, phys_block);
\r
923 /* Update i-node */
\r
925 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
\r
926 inode_ref->dirty = true;
\r
929 path_ptr->block.dirty = true;
\r
932 /* Set return values */
\r
933 *iblock = new_block_idx;
\r
934 *fblock = phys_block;
\r
937 * Put loaded blocks
\r
938 * starting from 1: 0 is a block with inode data
\r
940 for (i = 1; i <= path->depth; ++i) {
\r
941 if (path[i].block.lb_id){
\r
942 int r = ext4_block_set(inode_ref->fs->bdev, &path[i].block);
\r
948 /* Destroy temporary data structure */
\r