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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e25d78c was fe61181, checked in by Frantisek Princ <frantisek.princ@…>, 13 years ago

Debug messages removed

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