source: mainline/uspace/lib/ext4/libext4_extent.c@ b3cf946

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b3cf946 was 38542dc, checked in by Martin Decky <martin@…>, 13 years ago

ext4 code review and coding style cleanup

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