source: mainline/uspace/lib/ext4/src/extent.c@ 38d150e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 38d150e was 38d150e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Prefer to get memory allocation functions through the standard stdlib header.

  • Property mode set to 100644
File size: 29.8 KB
Line 
1/*
2 * Copyright (c) 2012 Frantisek Princ
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup libext4
30 * @{
31 */
32/**
33 * @file extent.c
34 * @brief Ext4 extent structures operations.
35 */
36
37#include <byteorder.h>
38#include <errno.h>
39#include <mem.h>
40#include <stdlib.h>
41#include "ext4/balloc.h"
42#include "ext4/extent.h"
43#include "ext4/inode.h"
44#include "ext4/superblock.h"
45
46/** Get logical number of the block covered by extent.
47 *
48 * @param extent Extent to load number from
49 *
50 * @return Logical number of the first block covered by extent
51 *
52 */
53uint32_t ext4_extent_get_first_block(ext4_extent_t *extent)
54{
55 return uint32_t_le2host(extent->first_block);
56}
57
58/** Set logical number of the first block covered by extent.
59 *
60 * @param extent Extent to set number to
61 * @param iblock Logical number of the first block covered by extent
62 *
63 */
64void ext4_extent_set_first_block(ext4_extent_t *extent, uint32_t iblock)
65{
66 extent->first_block = host2uint32_t_le(iblock);
67}
68
69/** Get number of blocks covered by extent.
70 *
71 * @param extent Extent to load count from
72 *
73 * @return Number of blocks covered by extent
74 *
75 */
76uint16_t ext4_extent_get_block_count(ext4_extent_t *extent)
77{
78 return uint16_t_le2host(extent->block_count);
79}
80
81/** Set number of blocks covered by extent.
82 *
83 * @param extent Extent to load count from
84 * @param count Number of blocks covered by extent
85 *
86 */
87void ext4_extent_set_block_count(ext4_extent_t *extent, uint16_t count)
88{
89 extent->block_count = host2uint16_t_le(count);
90}
91
92/** Get physical number of the first block covered by extent.
93 *
94 * @param extent Extent to load number
95 *
96 * @return Physical number of the first block covered by extent
97 *
98 */
99uint64_t ext4_extent_get_start(ext4_extent_t *extent)
100{
101 return ((uint64_t)uint16_t_le2host(extent->start_hi)) << 32 |
102 ((uint64_t)uint32_t_le2host(extent->start_lo));
103}
104
105/** Set physical number of the first block covered by extent.
106 *
107 * @param extent Extent to load number
108 * @param fblock Physical number of the first block covered by extent
109 *
110 */
111void ext4_extent_set_start(ext4_extent_t *extent, uint64_t fblock)
112{
113 extent->start_lo = host2uint32_t_le((fblock << 32) >> 32);
114 extent->start_hi = host2uint16_t_le((uint16_t)(fblock >> 32));
115}
116
117/** Get logical number of the block covered by extent index.
118 *
119 * @param index Extent index to load number from
120 *
121 * @return Logical number of the first block covered by extent index
122 *
123 */
124uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
125{
126 return uint32_t_le2host(index->first_block);
127}
128
129/** Set logical number of the block covered by extent index.
130 *
131 * @param index Extent index to set number to
132 * @param iblock Logical number of the first block covered by extent index
133 *
134 */
135void ext4_extent_index_set_first_block(ext4_extent_index_t *index,
136 uint32_t iblock)
137{
138 index->first_block = host2uint32_t_le(iblock);
139}
140
141/** Get physical number of block where the child node is located.
142 *
143 * @param index Extent index to load number from
144 *
145 * @return Physical number of the block with child node
146 *
147 */
148uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
149{
150 return ((uint64_t) uint16_t_le2host(index->leaf_hi)) << 32 |
151 ((uint64_t)uint32_t_le2host(index->leaf_lo));
152}
153
154/** Set physical number of block where the child node is located.
155 *
156 * @param index Extent index to set number to
157 * @param fblock Ohysical number of the block with child node
158 *
159 */
160void ext4_extent_index_set_leaf(ext4_extent_index_t *index, uint64_t fblock)
161{
162 index->leaf_lo = host2uint32_t_le((fblock << 32) >> 32);
163 index->leaf_hi = host2uint16_t_le((uint16_t) (fblock >> 32));
164}
165
166/** Get magic value from extent header.
167 *
168 * @param header Extent header to load value from
169 *
170 * @return Magic value of extent header
171 *
172 */
173uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
174{
175 return uint16_t_le2host(header->magic);
176}
177
178/** Set magic value to extent header.
179 *
180 * @param header Extent header to set value to
181 * @param magic Magic value of extent header
182 *
183 */
184void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
185{
186 header->magic = host2uint16_t_le(magic);
187}
188
189/** Get number of entries from extent header
190 *
191 * @param header Extent header to get value from
192 *
193 * @return Number of entries covered by extent header
194 *
195 */
196uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
197{
198 return uint16_t_le2host(header->entries_count);
199}
200
201/** Set number of entries to extent header
202 *
203 * @param header Extent header to set value to
204 * @param count Number of entries covered by extent header
205 *
206 */
207void ext4_extent_header_set_entries_count(ext4_extent_header_t *header,
208 uint16_t count)
209{
210 header->entries_count = host2uint16_t_le(count);
211}
212
213/** Get maximum number of entries from extent header
214 *
215 * @param header Extent header to get value from
216 *
217 * @return Maximum number of entries covered by extent header
218 *
219 */
220uint16_t ext4_extent_header_get_max_entries_count(ext4_extent_header_t *header)
221{
222 return uint16_t_le2host(header->max_entries_count);
223}
224
225/** Set maximum number of entries to extent header
226 *
227 * @param header Extent header to set value to
228 * @param max_count Maximum number of entries covered by extent header
229 *
230 */
231void ext4_extent_header_set_max_entries_count(ext4_extent_header_t *header,
232 uint16_t max_count)
233{
234 header->max_entries_count = host2uint16_t_le(max_count);
235}
236
237/** Get depth of extent subtree.
238 *
239 * @param header Extent header to get value from
240 *
241 * @return Depth of extent subtree
242 *
243 */
244uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
245{
246 return uint16_t_le2host(header->depth);
247}
248
249/** Set depth of extent subtree.
250 *
251 * @param header Extent header to set value to
252 * @param depth Depth of extent subtree
253 *
254 */
255void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
256{
257 header->depth = host2uint16_t_le(depth);
258}
259
260/** Get generation from extent header
261 *
262 * @param header Extent header to get value from
263 *
264 * @return Generation
265 *
266 */
267uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
268{
269 return uint32_t_le2host(header->generation);
270}
271
272/** Set generation to extent header
273 *
274 * @param header Extent header to set value to
275 * @param generation Generation
276 *
277 */
278void ext4_extent_header_set_generation(ext4_extent_header_t *header,
279 uint32_t generation)
280{
281 header->generation = host2uint32_t_le(generation);
282}
283
284/** Binary search in extent index node.
285 *
286 * @param header Extent header of index node
287 * @param index Output value - found index will be set here
288 * @param iblock Logical block number to find in index node
289 *
290 */
291static void ext4_extent_binsearch_idx(ext4_extent_header_t *header,
292 ext4_extent_index_t **index, uint32_t iblock)
293{
294 ext4_extent_index_t *r;
295 ext4_extent_index_t *l;
296 ext4_extent_index_t *m;
297
298 uint16_t entries_count =
299 ext4_extent_header_get_entries_count(header);
300
301 /* Initialize bounds */
302 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
303 r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1;
304
305 /* Do binary search */
306 while (l <= r) {
307 m = l + (r - l) / 2;
308 uint32_t first_block = ext4_extent_index_get_first_block(m);
309
310 if (iblock < first_block)
311 r = m - 1;
312 else
313 l = m + 1;
314 }
315
316 /* Set output value */
317 *index = l - 1;
318}
319
320/** Binary search in extent leaf node.
321 *
322 * @param header Extent header of leaf node
323 * @param extent Output value - found extent will be set here,
324 * or NULL if node is empty
325 * @param iblock Logical block number to find in leaf node
326 *
327 */
328static void ext4_extent_binsearch(ext4_extent_header_t *header,
329 ext4_extent_t **extent, uint32_t iblock)
330{
331 ext4_extent_t *r;
332 ext4_extent_t *l;
333 ext4_extent_t *m;
334
335 uint16_t entries_count =
336 ext4_extent_header_get_entries_count(header);
337
338 if (entries_count == 0) {
339 /* this leaf is empty */
340 *extent = NULL;
341 return;
342 }
343
344 /* Initialize bounds */
345 l = EXT4_EXTENT_FIRST(header) + 1;
346 r = EXT4_EXTENT_FIRST(header) + entries_count - 1;
347
348 /* Do binary search */
349 while (l <= r) {
350 m = l + (r - l) / 2;
351 uint32_t first_block = ext4_extent_get_first_block(m);
352
353 if (iblock < first_block)
354 r = m - 1;
355 else
356 l = m + 1;
357 }
358
359 /* Set output value */
360 *extent = l - 1;
361}
362
363/** Find physical block in the extent tree by logical block number.
364 *
365 * There is no need to save path in the tree during this algorithm.
366 *
367 * @param inode_ref I-node to load block from
368 * @param iblock Logical block number to find
369 * @param fblock Output value for physical block number
370 *
371 * @return Error code
372 *
373 */
374int ext4_extent_find_block(ext4_inode_ref_t *inode_ref, uint32_t iblock,
375 uint32_t *fblock)
376{
377 int rc = EOK;
378 /* Compute bound defined by i-node size */
379 uint64_t inode_size =
380 ext4_inode_get_size(inode_ref->fs->superblock, inode_ref->inode);
381
382 uint32_t block_size =
383 ext4_superblock_get_block_size(inode_ref->fs->superblock);
384
385 uint32_t last_idx = (inode_size - 1) / block_size;
386
387 /* Check if requested iblock is not over size of i-node */
388 if (iblock > last_idx) {
389 *fblock = 0;
390 return EOK;
391 }
392
393 block_t *block = NULL;
394
395 /* Walk through extent tree */
396 ext4_extent_header_t *header =
397 ext4_inode_get_extent_header(inode_ref->inode);
398
399 while (ext4_extent_header_get_depth(header) != 0) {
400 /* Search index in node */
401 ext4_extent_index_t *index;
402 ext4_extent_binsearch_idx(header, &index, iblock);
403
404 /* Load child node and set values for the next iteration */
405 uint64_t child = ext4_extent_index_get_leaf(index);
406
407 if (block != NULL) {
408 rc = block_put(block);
409 if (rc != EOK)
410 return rc;
411 }
412
413 rc = block_get(&block, inode_ref->fs->device, child,
414 BLOCK_FLAGS_NONE);
415 if (rc != EOK)
416 return rc;
417
418 header = (ext4_extent_header_t *)block->data;
419 }
420
421 /* Search extent in the leaf block */
422 ext4_extent_t* extent = NULL;
423 ext4_extent_binsearch(header, &extent, iblock);
424
425 /* Prevent empty leaf */
426 if (extent == NULL) {
427 *fblock = 0;
428 } else {
429 /* Compute requested physical block address */
430 uint32_t phys_block;
431 uint32_t first = ext4_extent_get_first_block(extent);
432 phys_block = ext4_extent_get_start(extent) + iblock - first;
433
434 *fblock = phys_block;
435 }
436
437 /* Cleanup */
438 if (block != NULL)
439 rc = block_put(block);
440
441 return rc;
442}
443
444/** Find extent for specified iblock.
445 *
446 * This function is used for finding block in the extent tree with
447 * saving the path through the tree for possible future modifications.
448 *
449 * @param inode_ref I-node to read extent tree from
450 * @param iblock Iblock to find extent for
451 * @param ret_path Output value for loaded path from extent tree
452 *
453 * @return Error code
454 *
455 */
456static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref, uint32_t iblock,
457 ext4_extent_path_t **ret_path)
458{
459 ext4_extent_header_t *eh =
460 ext4_inode_get_extent_header(inode_ref->inode);
461
462 uint16_t depth = ext4_extent_header_get_depth(eh);
463
464 ext4_extent_path_t *tmp_path;
465
466 /* Added 2 for possible tree growing */
467 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
468 if (tmp_path == NULL)
469 return ENOMEM;
470
471 /* Initialize structure for algorithm start */
472 tmp_path[0].block = inode_ref->block;
473 tmp_path[0].header = eh;
474
475 /* Walk through the extent tree */
476 uint16_t pos = 0;
477 int rc;
478 while (ext4_extent_header_get_depth(eh) != 0) {
479 /* Search index in index node by iblock */
480 ext4_extent_binsearch_idx(tmp_path[pos].header,
481 &tmp_path[pos].index, iblock);
482
483 tmp_path[pos].depth = depth;
484 tmp_path[pos].extent = NULL;
485
486 assert(tmp_path[pos].index != NULL);
487
488 /* Load information for the next iteration */
489 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
490
491 block_t *block;
492 rc = block_get(&block, inode_ref->fs->device, fblock,
493 BLOCK_FLAGS_NONE);
494 if (rc != EOK)
495 goto cleanup;
496
497 pos++;
498
499 eh = (ext4_extent_header_t *)block->data;
500 tmp_path[pos].block = block;
501 tmp_path[pos].header = eh;
502 }
503
504 tmp_path[pos].depth = 0;
505 tmp_path[pos].extent = NULL;
506 tmp_path[pos].index = NULL;
507
508 /* Find extent in the leaf node */
509 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
510 *ret_path = tmp_path;
511
512 return EOK;
513
514cleanup:
515 ;
516
517 int rc2 = EOK;
518
519 /*
520 * Put loaded blocks
521 * From 1: 0 is a block with inode data
522 */
523 for (uint16_t i = 1; i < tmp_path->depth; ++i) {
524 if (tmp_path[i].block) {
525 rc2 = block_put(tmp_path[i].block);
526 if (rc == EOK && rc2 != EOK)
527 rc = rc2;
528 }
529 }
530
531 /* Destroy temporary data structure */
532 free(tmp_path);
533
534 return rc;
535}
536
537/** Release extent and all data blocks covered by the extent.
538 *
539 * @param inode_ref I-node to release extent and block from
540 * @param extent Extent to release
541 *
542 * @return Error code
543 *
544 */
545static int ext4_extent_release(ext4_inode_ref_t *inode_ref,
546 ext4_extent_t *extent)
547{
548 /* Compute number of the first physical block to release */
549 uint64_t start = ext4_extent_get_start(extent);
550 uint16_t block_count = ext4_extent_get_block_count(extent);
551
552 return ext4_balloc_free_blocks(inode_ref, start, block_count);
553}
554
555/** Recursively release the whole branch of the extent tree.
556 *
557 * For each entry of the node release the subbranch and finally release
558 * the node. In the leaf node all extents will be released.
559 *
560 * @param inode_ref I-node where the branch is released
561 * @param index Index in the non-leaf node to be released
562 * with the whole subtree
563 *
564 * @return Error code
565 *
566 */
567static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
568 ext4_extent_index_t *index)
569{
570 uint32_t fblock = ext4_extent_index_get_leaf(index);
571
572 block_t* block;
573 int rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
574 if (rc != EOK)
575 return rc;
576
577 ext4_extent_header_t *header = block->data;
578
579 if (ext4_extent_header_get_depth(header)) {
580 /* The node is non-leaf, do recursion */
581 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
582
583 /* Release all subbranches */
584 for (uint32_t i = 0;
585 i < ext4_extent_header_get_entries_count(header);
586 ++i, ++idx) {
587 rc = ext4_extent_release_branch(inode_ref, idx);
588 if (rc != EOK)
589 return rc;
590 }
591 } else {
592 /* Leaf node reached */
593 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
594
595 /* Release all extents and stop recursion */
596 for (uint32_t i = 0;
597 i < ext4_extent_header_get_entries_count(header);
598 ++i, ++ext) {
599 rc = ext4_extent_release(inode_ref, ext);
600 if (rc != EOK)
601 return rc;
602 }
603 }
604
605 /* Release data block where the node was stored */
606
607 rc = block_put(block);
608 if (rc != EOK)
609 return rc;
610
611 return ext4_balloc_free_block(inode_ref, fblock);
612}
613
614/** Release all data blocks starting from specified logical block.
615 *
616 * @param inode_ref I-node to release blocks from
617 * @param iblock_from First logical block to release
618 *
619 */
620int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
621 uint32_t iblock_from)
622{
623 /* Find the first extent to modify */
624 ext4_extent_path_t *path;
625 int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
626 if (rc != EOK)
627 return rc;
628
629 /* Jump to last item of the path (extent) */
630 ext4_extent_path_t *path_ptr = path;
631 while (path_ptr->depth != 0)
632 path_ptr++;
633
634 assert(path_ptr->extent != NULL);
635
636 /* First extent maybe released partially */
637 uint32_t first_iblock =
638 ext4_extent_get_first_block(path_ptr->extent);
639 uint32_t first_fblock =
640 ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock;
641
642 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
643
644 uint16_t delete_count = block_count -
645 (ext4_extent_get_start(path_ptr->extent) - first_fblock);
646
647 /* Release all blocks */
648 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
649 if (rc != EOK)
650 goto cleanup;
651
652 /* Correct counter */
653 block_count -= delete_count;
654 ext4_extent_set_block_count(path_ptr->extent, block_count);
655
656 /* Initialize the following loop */
657 uint16_t entries =
658 ext4_extent_header_get_entries_count(path_ptr->header);
659 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
660 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
661
662 /* If first extent empty, release it */
663 if (block_count == 0)
664 entries--;
665
666 /* Release all successors of the first extent in the same node */
667 while (tmp_ext < stop_ext) {
668 first_fblock = ext4_extent_get_start(tmp_ext);
669 delete_count = ext4_extent_get_block_count(tmp_ext);
670
671 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
672 if (rc != EOK)
673 goto cleanup;
674
675 entries--;
676 tmp_ext++;
677 }
678
679 ext4_extent_header_set_entries_count(path_ptr->header, entries);
680 path_ptr->block->dirty = true;
681
682 /* If leaf node is empty, parent entry must be modified */
683 bool remove_parent_record = false;
684
685 /* Don't release root block (including inode data) !!! */
686 if ((path_ptr != path) && (entries == 0)) {
687 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
688 if (rc != EOK)
689 goto cleanup;
690
691 remove_parent_record = true;
692 }
693
694 /* Jump to the parent */
695 --path_ptr;
696
697 /* Release all successors in all tree levels */
698 while (path_ptr >= path) {
699 entries = ext4_extent_header_get_entries_count(path_ptr->header);
700 ext4_extent_index_t *index = path_ptr->index + 1;
701 ext4_extent_index_t *stop =
702 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
703
704 /* Correct entries count because of changes in the previous iteration */
705 if (remove_parent_record)
706 entries--;
707
708 /* Iterate over all entries and release the whole subtrees */
709 while (index < stop) {
710 rc = ext4_extent_release_branch(inode_ref, index);
711 if (rc != EOK)
712 goto cleanup;
713
714 ++index;
715 --entries;
716 }
717
718 ext4_extent_header_set_entries_count(path_ptr->header, entries);
719 path_ptr->block->dirty = true;
720
721 /* Free the node if it is empty */
722 if ((entries == 0) && (path_ptr != path)) {
723 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
724 if (rc != EOK)
725 goto cleanup;
726
727 /* Mark parent to be checked */
728 remove_parent_record = true;
729 } else
730 remove_parent_record = false;
731
732 --path_ptr;
733 }
734
735cleanup:
736 ;
737
738 int rc2 = EOK;
739
740 /*
741 * Put loaded blocks
742 * starting from 1: 0 is a block with inode data
743 */
744 for (uint16_t i = 1; i <= path->depth; ++i) {
745 if (path[i].block) {
746 rc2 = block_put(path[i].block);
747 if (rc == EOK && rc2 != EOK)
748 rc = rc2;
749 }
750 }
751
752 /* Destroy temporary data structure */
753 free(path);
754
755 return rc;
756}
757
758/** Append new extent to the i-node and do some splitting if necessary.
759 *
760 * @param inode_ref I-node to append extent to
761 * @param path Path in the extent tree for possible splitting
762 * @param last_path_item Input/output parameter for pointer to the last
763 * valid item in the extent tree path
764 * @param iblock Logical index of block to append extent for
765 *
766 * @return Error code
767 *
768 */
769static int ext4_extent_append_extent(ext4_inode_ref_t *inode_ref,
770 ext4_extent_path_t *path, uint32_t iblock)
771{
772 ext4_extent_path_t *path_ptr = path + path->depth;
773
774 uint32_t block_size =
775 ext4_superblock_get_block_size(inode_ref->fs->superblock);
776
777 /* Start splitting */
778 while (path_ptr > path) {
779 uint16_t entries =
780 ext4_extent_header_get_entries_count(path_ptr->header);
781 uint16_t limit =
782 ext4_extent_header_get_max_entries_count(path_ptr->header);
783
784 if (entries == limit) {
785 /* Full node - allocate block for new one */
786 uint32_t fblock;
787 int rc = ext4_balloc_alloc_block(inode_ref, &fblock);
788 if (rc != EOK)
789 return rc;
790
791 block_t *block;
792 rc = block_get(&block, inode_ref->fs->device, fblock,
793 BLOCK_FLAGS_NOREAD);
794 if (rc != EOK) {
795 ext4_balloc_free_block(inode_ref, fblock);
796 return rc;
797 }
798
799 /* Put back not modified old block */
800 rc = block_put(path_ptr->block);
801 if (rc != EOK) {
802 ext4_balloc_free_block(inode_ref, fblock);
803 block_put(block);
804 return rc;
805 }
806
807 /* Initialize newly allocated block and remember it */
808 memset(block->data, 0, block_size);
809 path_ptr->block = block;
810
811 /* Update pointers in extent path structure */
812 path_ptr->header = block->data;
813 if (path_ptr->depth) {
814 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
815 ext4_extent_index_set_first_block(path_ptr->index, iblock);
816 ext4_extent_index_set_leaf(path_ptr->index, (path_ptr + 1)->block->lba);
817 limit = (block_size - sizeof(ext4_extent_header_t)) /
818 sizeof(ext4_extent_index_t);
819 } else {
820 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
821 ext4_extent_set_first_block(path_ptr->extent, iblock);
822 limit = (block_size - sizeof(ext4_extent_header_t)) /
823 sizeof(ext4_extent_t);
824 }
825
826 /* Initialize on-disk structure (header) */
827 ext4_extent_header_set_entries_count(path_ptr->header, 1);
828 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
829 ext4_extent_header_set_magic(path_ptr->header, EXT4_EXTENT_MAGIC);
830 ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth);
831 ext4_extent_header_set_generation(path_ptr->header, 0);
832
833 path_ptr->block->dirty = true;
834
835 /* Jump to the preceeding item */
836 path_ptr--;
837 } else {
838 /* Node with free space */
839 if (path_ptr->depth) {
840 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
841 ext4_extent_index_set_first_block(path_ptr->index, iblock);
842 ext4_extent_index_set_leaf(path_ptr->index, (path_ptr + 1)->block->lba);
843 } else {
844 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
845 ext4_extent_set_first_block(path_ptr->extent, iblock);
846 }
847
848 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
849 path_ptr->block->dirty = true;
850
851 /* No more splitting needed */
852 return EOK;
853 }
854 }
855
856 assert(path_ptr == path);
857
858 /* Should be the root split too? */
859
860 uint16_t entries = ext4_extent_header_get_entries_count(path->header);
861 uint16_t limit = ext4_extent_header_get_max_entries_count(path->header);
862
863 if (entries == limit) {
864 uint32_t new_fblock;
865 int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
866 if (rc != EOK)
867 return rc;
868
869 block_t *block;
870 rc = block_get(&block, inode_ref->fs->device, new_fblock,
871 BLOCK_FLAGS_NOREAD);
872 if (rc != EOK)
873 return rc;
874
875 /* Initialize newly allocated block */
876 memset(block->data, 0, block_size);
877
878 /* Move data from root to the new block */
879 memcpy(block->data, inode_ref->inode->blocks,
880 EXT4_INODE_BLOCKS * sizeof(uint32_t));
881
882 /* Data block is initialized */
883
884 block_t *root_block = path->block;
885 uint16_t root_depth = path->depth;
886 ext4_extent_header_t *root_header = path->header;
887
888 /* Make space for tree growing */
889 ext4_extent_path_t *new_root = path;
890 ext4_extent_path_t *old_root = path + 1;
891
892 size_t nbytes = sizeof(ext4_extent_path_t) * (path->depth + 1);
893 memmove(old_root, new_root, nbytes);
894 memset(new_root, 0, sizeof(ext4_extent_path_t));
895
896 /* Update old root structure */
897 old_root->block = block;
898 old_root->header = (ext4_extent_header_t *)block->data;
899
900 /* Add new entry and update limit for entries */
901 if (old_root->depth) {
902 limit = (block_size - sizeof(ext4_extent_header_t)) /
903 sizeof(ext4_extent_index_t);
904 old_root->index = EXT4_EXTENT_FIRST_INDEX(old_root->header) + entries;
905 ext4_extent_index_set_first_block(old_root->index, iblock);
906 ext4_extent_index_set_leaf(old_root->index, (old_root + 1)->block->lba);
907 old_root->extent = NULL;
908 } else {
909 limit = (block_size - sizeof(ext4_extent_header_t)) /
910 sizeof(ext4_extent_t);
911 old_root->extent = EXT4_EXTENT_FIRST(old_root->header) + entries;
912 ext4_extent_set_first_block(old_root->extent, iblock);
913 old_root->index = NULL;
914 }
915
916 ext4_extent_header_set_entries_count(old_root->header, entries + 1);
917 ext4_extent_header_set_max_entries_count(old_root->header, limit);
918
919 old_root->block->dirty = true;
920
921 /* Re-initialize new root metadata */
922 new_root->depth = root_depth + 1;
923 new_root->block = root_block;
924 new_root->header = root_header;
925 new_root->extent = NULL;
926 new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header);
927
928 ext4_extent_header_set_depth(new_root->header, new_root->depth);
929
930 /* Create new entry in root */
931 ext4_extent_header_set_entries_count(new_root->header, 1);
932 ext4_extent_index_set_first_block(new_root->index, 0);
933 ext4_extent_index_set_leaf(new_root->index, new_fblock);
934
935 new_root->block->dirty = true;
936 } else {
937 if (path->depth) {
938 path->index = EXT4_EXTENT_FIRST_INDEX(path->header) + entries;
939 ext4_extent_index_set_first_block(path->index, iblock);
940 ext4_extent_index_set_leaf(path->index, (path + 1)->block->lba);
941 } else {
942 path->extent = EXT4_EXTENT_FIRST(path->header) + entries;
943 ext4_extent_set_first_block(path->extent, iblock);
944 }
945
946 ext4_extent_header_set_entries_count(path->header, entries + 1);
947 path->block->dirty = true;
948 }
949
950 return EOK;
951}
952
953/** Append data block to the i-node.
954 *
955 * This function allocates data block, tries to append it
956 * to some existing extent or creates new extents.
957 * It includes possible extent tree modifications (splitting).
958 *<
959 * @param inode_ref I-node to append block to
960 * @param iblock Output logical number of newly allocated block
961 * @param fblock Output physical block address of newly allocated block
962 *
963 * @return Error code
964 *
965 */
966int ext4_extent_append_block(ext4_inode_ref_t *inode_ref, uint32_t *iblock,
967 uint32_t *fblock, bool update_size)
968{
969 ext4_superblock_t *sb = inode_ref->fs->superblock;
970 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
971 uint32_t block_size = ext4_superblock_get_block_size(sb);
972
973 /* Calculate number of new logical block */
974 uint32_t new_block_idx = 0;
975 if (inode_size > 0) {
976 if ((inode_size % block_size) != 0)
977 inode_size += block_size - (inode_size % block_size);
978
979 new_block_idx = inode_size / block_size;
980 }
981
982 /* Load the nearest leaf (with extent) */
983 ext4_extent_path_t *path;
984 int rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
985 if (rc != EOK)
986 return rc;
987
988 /* Jump to last item of the path (extent) */
989 ext4_extent_path_t *path_ptr = path;
990 while (path_ptr->depth != 0)
991 path_ptr++;
992
993 /* Add new extent to the node if not present */
994 if (path_ptr->extent == NULL)
995 goto append_extent;
996
997 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
998 uint16_t block_limit = (1 << 15);
999
1000 uint32_t phys_block = 0;
1001 if (block_count < block_limit) {
1002 /* There is space for new block in the extent */
1003 if (block_count == 0) {
1004 /* Existing extent is empty */
1005 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1006 if (rc != EOK)
1007 goto finish;
1008
1009 /* Initialize extent */
1010 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1011 ext4_extent_set_start(path_ptr->extent, phys_block);
1012 ext4_extent_set_block_count(path_ptr->extent, 1);
1013
1014 /* Update i-node */
1015 if (update_size) {
1016 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1017 inode_ref->dirty = true;
1018 }
1019
1020 path_ptr->block->dirty = true;
1021
1022 goto finish;
1023 } else {
1024 /* Existing extent contains some blocks */
1025 phys_block = ext4_extent_get_start(path_ptr->extent);
1026 phys_block += ext4_extent_get_block_count(path_ptr->extent);
1027
1028 /* Check if the following block is free for allocation */
1029 bool free;
1030 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
1031 if (rc != EOK)
1032 goto finish;
1033
1034 if (!free) {
1035 /* Target is not free, new block must be appended to new extent */
1036 goto append_extent;
1037 }
1038
1039 /* Update extent */
1040 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
1041
1042 /* Update i-node */
1043 if (update_size) {
1044 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1045 inode_ref->dirty = true;
1046 }
1047
1048 path_ptr->block->dirty = true;
1049
1050 goto finish;
1051 }
1052 }
1053
1054
1055append_extent:
1056 /* Append new extent to the tree */
1057 phys_block = 0;
1058
1059 /* Allocate new data block */
1060 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1061 if (rc != EOK)
1062 goto finish;
1063
1064 /* Append extent for new block (includes tree splitting if needed) */
1065 rc = ext4_extent_append_extent(inode_ref, path, new_block_idx);
1066 if (rc != EOK) {
1067 ext4_balloc_free_block(inode_ref, phys_block);
1068 goto finish;
1069 }
1070
1071 uint32_t tree_depth = ext4_extent_header_get_depth(path->header);
1072 path_ptr = path + tree_depth;
1073
1074 /* Initialize newly created extent */
1075 ext4_extent_set_block_count(path_ptr->extent, 1);
1076 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1077 ext4_extent_set_start(path_ptr->extent, phys_block);
1078
1079 /* Update i-node */
1080 if (update_size) {
1081 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1082 inode_ref->dirty = true;
1083 }
1084
1085 path_ptr->block->dirty = true;
1086
1087finish:
1088 ;
1089
1090 int rc2 = EOK;
1091
1092 /* Set return values */
1093 *iblock = new_block_idx;
1094 *fblock = phys_block;
1095
1096 /*
1097 * Put loaded blocks
1098 * starting from 1: 0 is a block with inode data
1099 */
1100 for (uint16_t i = 1; i <= path->depth; ++i) {
1101 if (path[i].block) {
1102 rc2 = block_put(path[i].block);
1103 if (rc == EOK && rc2 != EOK)
1104 rc = rc2;
1105 }
1106 }
1107
1108 /* Destroy temporary data structure */
1109 free(path);
1110
1111 return rc;
1112}
1113
1114/**
1115 * @}
1116 */
Note: See TracBrowser for help on using the repository browser.