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

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

fixed bug in releasing extents

  • Property mode set to 100644
File size: 28.2 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 // Check trivial situation
265 if (entries_count == 1) {
266 *index = EXT4_EXTENT_FIRST_INDEX(header);
267 return;
268 }
269
270 // Initialize bounds
271 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
272 r = l + entries_count - 1;
273
274 // Do binary search
275 while (l <= r) {
276 m = l + (r - l) / 2;
277 uint32_t first_block = ext4_extent_index_get_first_block(m);
278 if (iblock < first_block) {
279 r = m - 1;
280 } else {
281 l = m + 1;
282 }
283 }
284
285 // Set output value
286 *index = l - 1;
287}
288
289/** Binary search in extent leaf node.
290 * @param header extent header of leaf node
291 * @param extent output value - found extent will be set here,
292 * or NULL if node is empty
293 * @param iblock logical block number to find in leaf node
294 *
295 */
296static void ext4_extent_binsearch(ext4_extent_header_t *header,
297 ext4_extent_t **extent, uint32_t iblock)
298{
299 ext4_extent_t *r, *l, *m;
300
301 uint16_t entries_count = ext4_extent_header_get_entries_count(header);
302
303 if (entries_count == 0) {
304 // this leaf is empty
305 *extent = NULL;
306 return;
307 }
308
309 // Check trivial situation
310 if (entries_count == 1) {
311 *extent = EXT4_EXTENT_FIRST(header);
312 return;
313 }
314
315 // Initialize bounds
316 l = EXT4_EXTENT_FIRST(header) + 1;
317 r = l + entries_count - 1;
318
319 // Do binary search
320 while (l < r) {
321 m = l + (r - l) / 2;
322 uint32_t first_block = ext4_extent_get_first_block(m);
323 if (iblock < first_block) {
324 r = m - 1;
325 } else {
326 l = m + 1;
327 }
328 }
329
330 // Set output value
331 *extent = l - 1;
332}
333
334/** Find physical block in the extent tree by logical block number.
335 *
336 * There is no need to save path in the tree during this algorithm.
337 *
338 * @param inode_ref i-node to load block from
339 * @param iblock logical block number to find
340 * @param fblock output value for physical block number
341 * @return error code
342 */
343int ext4_extent_find_block(ext4_inode_ref_t *inode_ref,
344 uint32_t iblock, uint32_t *fblock)
345{
346 int rc;
347
348 // Compute bound defined by i-node size
349 uint64_t inode_size = ext4_inode_get_size(
350 inode_ref->fs->superblock, inode_ref->inode);
351
352 uint32_t block_size = ext4_superblock_get_block_size(
353 inode_ref->fs->superblock);
354
355 uint32_t last_idx = (inode_size - 1) / block_size;
356
357 // Check if requested iblock is not over size of i-node
358 if (iblock > last_idx) {
359 *fblock = 0;
360 return EOK;
361 }
362
363 block_t* block = NULL;
364
365 // Walk through extent tree
366 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode_ref->inode);
367 while (ext4_extent_header_get_depth(header) != 0) {
368
369 // Search index in node
370 ext4_extent_index_t *index;
371 ext4_extent_binsearch_idx(header, &index, iblock);
372
373 // Load child node and set values for the next iteration
374 uint64_t child = ext4_extent_index_get_leaf(index);
375
376 if (block != NULL) {
377 block_put(block);
378 }
379
380 rc = block_get(&block, inode_ref->fs->device, child, BLOCK_FLAGS_NONE);
381 if (rc != EOK) {
382 return rc;
383 }
384
385 header = (ext4_extent_header_t *)block->data;
386 }
387
388 // Search extent in the leaf block
389 ext4_extent_t* extent = NULL;
390 ext4_extent_binsearch(header, &extent, iblock);
391
392 // Prevent empty leaf
393 if (extent == NULL) {
394 *fblock = 0;
395 } else {
396
397 // Compute requested physical block address
398 uint32_t phys_block;
399 phys_block = ext4_extent_get_start(extent) + iblock;
400 phys_block -= ext4_extent_get_first_block(extent);
401
402 *fblock = phys_block;
403 }
404
405 // Cleanup
406 if (block != NULL) {
407 block_put(block);
408 }
409
410 return EOK;
411}
412
413
414/** Find extent for specified iblock.
415 *
416 * This function is used for finding block in the extent tree with
417 * saving the path through the tree for possible future modifications.
418 *
419 * @param inode_ref i-node to read extent tree from
420 * @param iblock iblock to find extent for
421 * @param ret_path output value for loaded path from extent tree
422 * @return error code
423 */
424static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref,
425 uint32_t iblock, ext4_extent_path_t **ret_path)
426{
427 int rc;
428
429 ext4_extent_header_t *eh =
430 ext4_inode_get_extent_header(inode_ref->inode);
431
432 uint16_t depth = ext4_extent_header_get_depth(eh);
433
434 ext4_extent_path_t *tmp_path;
435
436 // Added 2 for possible tree growing
437 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
438 if (tmp_path == NULL) {
439 return ENOMEM;
440 }
441
442 // Initialize structure for algorithm start
443 tmp_path[0].block = inode_ref->block;
444 tmp_path[0].header = eh;
445
446 // Walk through the extent tree
447 uint16_t pos = 0;
448 while (ext4_extent_header_get_depth(eh) != 0) {
449
450 // Search index in index node by iblock
451 ext4_extent_binsearch_idx(tmp_path[pos].header, &tmp_path[pos].index, iblock);
452
453 tmp_path[pos].depth = depth;
454 tmp_path[pos].extent = NULL;
455
456 assert(tmp_path[pos].index != NULL);
457
458 // Load information for the next iteration
459 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
460
461 block_t *block;
462 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
463 if (rc != EOK) {
464 goto cleanup;
465 }
466
467 pos++;
468
469 eh = (ext4_extent_header_t *)block->data;
470 tmp_path[pos].block = block;
471 tmp_path[pos].header = eh;
472
473 }
474
475 tmp_path[pos].depth = 0;
476 tmp_path[pos].extent = NULL;
477 tmp_path[pos].index = NULL;
478
479 // Find extent in the leaf node
480 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
481
482 *ret_path = tmp_path;
483
484 return EOK;
485
486cleanup:
487 // Put loaded blocks
488 // From 1 -> 0 is a block with inode data
489 for (uint16_t i = 1; i < tmp_path->depth; ++i) {
490 if (tmp_path[i].block) {
491 block_put(tmp_path[i].block);
492 }
493 }
494
495 // Destroy temporary data structure
496 free(tmp_path);
497
498 return rc;
499}
500
501/** Release extent and all data blocks covered by the extent.
502 *
503 * @param inode_ref i-node to release extent and block from
504 * @param extent extent to release
505 * @return error code
506 */
507static int ext4_extent_release(
508 ext4_inode_ref_t *inode_ref, ext4_extent_t *extent)
509{
510 int rc;
511
512 // Compute number of the first physical block to release
513 uint64_t start = ext4_extent_get_start(extent);
514 uint16_t block_count = ext4_extent_get_block_count(extent);
515
516 rc = ext4_balloc_free_blocks(inode_ref, start, block_count);
517 if (rc != EOK) {
518 EXT4FS_DBG("Error in releasing data blocks");
519 return rc;
520 }
521
522 return EOK;
523}
524
525/** Recursively release the whole branch of the extent tree.
526 *
527 * For each entry of the node release the subbranch and finally release
528 * the node. In the leaf node all extents will be released.
529 *
530 * @param inode_ref i-node where the branch is released
531 * @param index index in the non-leaf node to be released
532 * with the whole subtree
533 * @return error code
534 */
535static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
536 ext4_extent_index_t *index)
537{
538 int rc;
539
540 block_t* block;
541
542 uint32_t fblock = ext4_extent_index_get_leaf(index);
543
544 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
545 if (rc != EOK) {
546 EXT4FS_DBG("ERROR get_block");
547 return rc;
548 }
549
550 ext4_extent_header_t *header = block->data;
551
552 if (ext4_extent_header_get_depth(header)) {
553
554 // The node is non-leaf, do recursion
555
556 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
557
558 // Release all subbranches
559 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++idx) {
560 rc = ext4_extent_release_branch(inode_ref, idx);
561 if (rc != EOK) {
562 EXT4FS_DBG("error recursion");
563 return rc;
564 }
565 }
566 } else {
567
568 // Leaf node reached
569 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
570
571 // Release all extents and stop recursion
572
573 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++ext) {
574 rc = ext4_extent_release(inode_ref, ext);
575 if (rc != EOK) {
576 EXT4FS_DBG("error recursion");
577 return rc;
578 }
579 }
580 }
581
582 // Release data block where the node was stored
583
584 rc = block_put(block);
585 if (rc != EOK) {
586 EXT4FS_DBG("ERROR put_block");
587 return rc;
588 }
589
590 ext4_balloc_free_block(inode_ref, fblock);
591
592 return EOK;
593}
594
595/** Release all data blocks starting from specified logical block.
596 *
597 * @param inode_ref i-node to release blocks from
598 * @param iblock_from first logical block to release
599 */
600int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
601 uint32_t iblock_from)
602{
603 int rc = EOK;
604
605 // Find the first extent to modify
606 ext4_extent_path_t *path;
607 rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
608 if (rc != EOK) {
609 return rc;
610 }
611
612 // Jump to last item of the path (extent)
613 ext4_extent_path_t *path_ptr = path;
614 while (path_ptr->depth != 0) {
615 path_ptr++;
616 }
617
618 assert(path_ptr->extent != NULL);
619
620 // First extent maybe released partially
621 uint32_t first_fblock;
622 first_fblock = ext4_extent_get_start(path_ptr->extent) + iblock_from;
623 first_fblock -= ext4_extent_get_first_block(path_ptr->extent);
624
625 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
626
627 uint16_t delete_count = block_count - first_fblock +
628 ext4_extent_get_start(path_ptr->extent);
629
630 // Release all blocks
631 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
632 if (rc != EOK) {
633 goto cleanup;
634 }
635
636 // Correct counter
637 block_count -= delete_count;
638 ext4_extent_set_block_count(path_ptr->extent, block_count);
639
640 // Initialize the following loop
641 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
642 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
643 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
644
645 // If first extent empty, release it
646 if (block_count == 0) {
647 entries--;
648 ext4_extent_header_set_entries_count(path_ptr->header, entries);
649 }
650
651 // Release all successors of the first extent in the same node
652 while (tmp_ext < stop_ext) {
653 first_fblock = ext4_extent_get_start(tmp_ext);
654 delete_count = ext4_extent_get_block_count(tmp_ext);
655
656 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
657 if (rc != EOK) {
658 goto cleanup;
659 }
660
661 entries--;
662 ext4_extent_header_set_entries_count(path_ptr->header, entries);
663
664 tmp_ext++;
665 }
666
667 // If leaf node is empty, the whole tree must be checked and the node will be released
668 bool remove_parent_record = false;
669
670 // Don't release root block (including inode data) !!!
671 if ((path_ptr != path) && (entries == 0)) {
672 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
673 if (rc != EOK) {
674 goto cleanup;
675 }
676 remove_parent_record = true;
677 }
678
679 // Jump to the parent
680 --path_ptr;
681
682 // release all successors in all tree levels
683 while (path_ptr >= path) {
684 entries = ext4_extent_header_get_entries_count(path_ptr->header);
685 ext4_extent_index_t *index = path_ptr->index + 1;
686 ext4_extent_index_t *stop =
687 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
688
689 // Correct entry because of changes in the previous iteration
690 if (remove_parent_record) {
691 entries--;
692 }
693
694 // Iterate over all entries and release the whole subtrees
695 while (index < stop) {
696 rc = ext4_extent_release_branch(inode_ref, index);
697 if (rc != EOK) {
698 goto cleanup;
699 }
700 ++index;
701 --entries;
702 }
703
704 ext4_extent_header_set_entries_count(path_ptr->header, entries);
705
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
715 // Mark parent to be checked
716 remove_parent_record = true;
717 } else {
718 remove_parent_record = false;
719 }
720
721 --path_ptr;
722 }
723
724
725cleanup:
726 // Put loaded blocks
727 // From 1 -> 0 is a block with inode data
728 for (uint16_t i = 1; i <= path->depth; ++i) {
729 if (path[i].block) {
730 block_put(path[i].block);
731 }
732 }
733
734 // Destroy temporary data structure
735 free(path);
736
737 return rc;
738}
739
740
741/** Append new extent to the i-node and do some splitting if necessary.
742 *
743 * @param inode_ref i-node to append extent to
744 * @param path path in the extent tree for possible splitting
745 * @param last_path_item input/output parameter for pointer to the last
746 * valid item in the extent tree path
747 * @param iblock logical index of block to append extent for
748 * @return error code
749 */
750static int ext4_extent_append_extent(ext4_inode_ref_t *inode_ref,
751 ext4_extent_path_t *path, ext4_extent_path_t **last_path_item,
752 uint32_t iblock)
753{
754 EXT4FS_DBG("");
755 int rc;
756
757 ext4_extent_path_t *path_ptr = *last_path_item;
758
759 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
760 uint16_t limit = ext4_extent_header_get_max_entries_count(path_ptr->header);
761
762 // Trivial way - no splitting
763 if (entries < limit) {
764 EXT4FS_DBG("adding extent entry");
765
766 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
767 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
768 ext4_extent_set_block_count(path_ptr->extent, 0);
769 ext4_extent_set_first_block(path_ptr->extent, iblock);
770 ext4_extent_set_start(path_ptr->extent, 0);
771 path_ptr->block->dirty = true;
772
773 return EOK;
774 }
775
776 uint32_t block_size =
777 ext4_superblock_get_block_size(inode_ref->fs->superblock);
778
779 // Trivial tree - grow (extents were in root node)
780 if (path_ptr == path) {
781
782 uint32_t new_fblock;
783 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
784 if (rc != EOK) {
785 EXT4FS_DBG("error in block allocation");
786 return rc;
787 }
788
789 block_t *block;
790 rc = block_get(&block, inode_ref->fs->device,
791 new_fblock, BLOCK_FLAGS_NOREAD);
792 if (rc != EOK) {
793 EXT4FS_DBG("error in block_get");
794 return rc;
795 }
796
797 memset(block->data, 0, block_size);
798
799 // Move data from root to the new block
800 memcpy(block->data, inode_ref->inode->blocks,
801 EXT4_INODE_BLOCKS * sizeof(uint32_t));
802
803 path_ptr++;
804 path_ptr->block = block;
805 path_ptr->header = (ext4_extent_header_t *)block->data;
806 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
807 path_ptr->index = NULL;
808
809 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
810 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
811 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
812 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
813 sizeof(ext4_extent_t);
814 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
815
816 // Modify root (in inode)
817 path->depth = 1;
818 path->extent = NULL;
819 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
820
821 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
822 ext4_extent_header_set_entries_count(path->header, 1);
823
824 ext4_extent_index_set_first_block(path->index, 0);
825 ext4_extent_index_set_leaf(path->index, new_fblock);
826
827 path_ptr->block->dirty = true;
828 path->block->dirty = true;
829
830 *last_path_item = path_ptr;
831
832 return EOK;
833 }
834
835 assert(false);
836
837 // start splitting
838 uint32_t fblock = 0;
839 while (path_ptr > path) {
840
841 // TODO
842
843 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
844 if (rc != EOK) {
845 return rc;
846 }
847
848 block_t *block;
849 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
850 if (rc != EOK) {
851 ext4_balloc_free_block(inode_ref, fblock);
852 return rc;
853 }
854
855 // Init block
856 memset(block->data, 0, block_size);
857
858 // Not modified
859 block_put(path_ptr->block);
860 path_ptr->block = block;
861
862 path_ptr->header = block->data;
863
864 if (path_ptr->depth) {
865 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
866 } else {
867 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
868 }
869
870 path_ptr--;
871 }
872
873 // If splitting reached root node
874 if (path_ptr == path) {
875
876 uint32_t new_fblock;
877 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
878 if (rc != EOK) {
879 EXT4FS_DBG("error in block allocation");
880 return rc;
881 }
882
883 block_t *block;
884 rc = block_get(&block, inode_ref->fs->device,
885 new_fblock, BLOCK_FLAGS_NOREAD);
886 if (rc != EOK) {
887 EXT4FS_DBG("error in block_get");
888 return rc;
889 }
890
891 memset(block->data, 0, block_size);
892
893 // Move data from root to the new block
894 memcpy(block->data, inode_ref->inode->blocks,
895 EXT4_INODE_BLOCKS * sizeof(uint32_t));
896
897 path_ptr++;
898 path_ptr->block = block;
899 path_ptr->header = (ext4_extent_header_t *)block->data;
900 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
901 path_ptr->index = NULL;
902
903 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
904 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
905 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
906 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
907 sizeof(ext4_extent_t);
908 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
909
910 // Modify root (in inode)
911 path->depth = 1;
912 path->extent = NULL;
913 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
914
915 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
916 ext4_extent_header_set_entries_count(path->header, 1);
917
918 ext4_extent_index_set_first_block(path->index, 0);
919 ext4_extent_index_set_leaf(path->index, new_fblock);
920
921 path_ptr->block->dirty = true;
922 path->block->dirty = true;
923
924 *last_path_item = path_ptr;
925
926 return EOK;
927 }
928
929 return EOK;
930}
931
932/** Append data block to the i-node.
933 *
934 * This function allocates data block, tries to append it
935 * to some existing extent or creates new extents.
936 * It includes possible extent tree modifications (splitting).
937 *
938 * @param inode_ref i-node to append block to
939 * @param iblock output logical number of newly allocated block
940 * @param fblock output physical block address of newly allocated block
941 * @return error code
942 */
943int ext4_extent_append_block(ext4_inode_ref_t *inode_ref,
944 uint32_t *iblock, uint32_t *fblock)
945{
946 EXT4FS_DBG("");
947 int rc = EOK;
948
949 ext4_superblock_t *sb = inode_ref->fs->superblock;
950 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
951 uint32_t block_size = ext4_superblock_get_block_size(sb);
952
953 // Calculate number of new logical block
954 uint32_t new_block_idx = 0;
955 if (inode_size > 0) {
956 if ((inode_size % block_size) != 0) {
957 inode_size += block_size - (inode_size % block_size);
958 }
959 new_block_idx = inode_size / block_size;
960 }
961
962 // Load the nearest leaf (with extent)
963 ext4_extent_path_t *path;
964 rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
965 if (rc != EOK) {
966 return rc;
967 }
968
969 // Jump to last item of the path (extent)
970 ext4_extent_path_t *path_ptr = path;
971 while (path_ptr->depth != 0) {
972 path_ptr++;
973 }
974
975 // Add new extent to the node if not present
976 if (path_ptr->extent == NULL) {
977 goto append_extent;
978 }
979
980 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
981 uint16_t block_limit = (1 << 15);
982
983 uint32_t phys_block = 0;
984 if (block_count < block_limit) {
985
986 // There is space for new block in the extent
987
988 if (block_count == 0) {
989
990 // Existing extent is empty
991
992 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
993 if (rc != EOK) {
994 goto finish;
995 }
996
997 // Initialize extent
998 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
999 ext4_extent_set_start(path_ptr->extent, phys_block);
1000 ext4_extent_set_block_count(path_ptr->extent, 1);
1001
1002 // Update i-node
1003 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1004 inode_ref->dirty = true;
1005
1006 path_ptr->block->dirty = true;
1007
1008 goto finish;
1009 } else {
1010
1011 // Existing extent contains some blocks
1012
1013 phys_block = ext4_extent_get_start(path_ptr->extent);
1014 phys_block += ext4_extent_get_block_count(path_ptr->extent);
1015
1016 // Check if the following block is free for allocation
1017 bool free;
1018 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
1019 if (rc != EOK) {
1020 goto finish;
1021 }
1022
1023 if (! free) {
1024 // target is not free, new block must be appended to new extent
1025 goto append_extent;
1026 }
1027
1028
1029 // Update extent
1030 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
1031
1032 // Update i-node
1033 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1034 inode_ref->dirty = true;
1035
1036 path_ptr->block->dirty = true;
1037
1038 goto finish;
1039 }
1040 }
1041
1042// Append new extent to the tree
1043append_extent:
1044
1045 phys_block = 0;
1046
1047 // Allocate new data block
1048 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1049 if (rc != EOK) {
1050 EXT4FS_DBG("error in block allocation, rc = \%d", rc);
1051 goto finish;
1052 }
1053
1054 // Append extent for new block (includes tree splitting if needed)
1055 rc = ext4_extent_append_extent(inode_ref, path, &path_ptr, new_block_idx);
1056 if (rc != EOK) {
1057 ext4_balloc_free_block(inode_ref, phys_block);
1058 goto finish;
1059 }
1060
1061 // Initialize newly created extent
1062 ext4_extent_set_block_count(path_ptr->extent, 1);
1063 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1064 ext4_extent_set_start(path_ptr->extent, phys_block);
1065
1066 // Update i-node
1067 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1068 inode_ref->dirty = true;
1069
1070 path_ptr->block->dirty = true;
1071
1072
1073finish:
1074 // Set return values
1075 *iblock = new_block_idx;
1076 *fblock = phys_block;
1077
1078 // Put loaded blocks
1079 // From 1 -> 0 is a block with inode data
1080 for (uint16_t i = 1; i <= path->depth; ++i) {
1081 if (path[i].block) {
1082 block_put(path[i].block);
1083 }
1084 }
1085
1086 // Destroy temporary data structure
1087 free(path);
1088
1089 return rc;
1090}
1091
1092/**
1093 * @}
1094 */
Note: See TracBrowser for help on using the repository browser.