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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 29.7 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 */
374errno_t ext4_extent_find_block(ext4_inode_ref_t *inode_ref, uint32_t iblock,
375 uint32_t *fblock)
376{
377 errno_t 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 errno_t 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 errno_t 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 errno_t 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 errno_t 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 errno_t 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 errno_t 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 */
620errno_t 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 errno_t 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 errno_t 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 errno_t 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 errno_t 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 errno_t 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 */
966errno_t 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 errno_t 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 errno_t 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.