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

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

comments on getters and setters used for extent structure

  • Property mode set to 100644
File size: 22.0 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 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 load number from
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// TODO start comments here
106
107uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
108{
109 return uint32_t_le2host(index->first_block);
110}
111
112void ext4_extent_index_set_first_block(ext4_extent_index_t *index,
113 uint32_t first)
114{
115 index->first_block = host2uint32_t_le(first);
116}
117
118uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
119{
120 return ((uint64_t)uint16_t_le2host(index->leaf_hi)) << 32 |
121 ((uint64_t)uint32_t_le2host(index->leaf_lo));
122}
123
124void ext4_extent_index_set_leaf(ext4_extent_index_t *index, uint64_t leaf)
125{
126 index->leaf_lo = host2uint32_t_le((leaf << 32) >> 32);
127 index->leaf_hi = host2uint16_t_le((uint16_t)(leaf >> 32));
128}
129
130uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
131{
132 return uint16_t_le2host(header->magic);
133}
134
135void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
136{
137 header->magic = host2uint16_t_le(magic);
138}
139
140uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
141{
142 return uint16_t_le2host(header->entries_count);
143}
144
145void ext4_extent_header_set_entries_count(ext4_extent_header_t *header,
146 uint16_t count)
147{
148 header->entries_count = host2uint16_t_le(count);
149}
150
151uint16_t ext4_extent_header_get_max_entries_count(ext4_extent_header_t *header)
152{
153 return uint16_t_le2host(header->max_entries_count);
154}
155
156void ext4_extent_header_set_max_entries_count(ext4_extent_header_t *header,
157 uint16_t max_count)
158{
159 header->max_entries_count = host2uint16_t_le(max_count);
160}
161
162uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
163{
164 return uint16_t_le2host(header->depth);
165}
166
167void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
168{
169 header->depth = host2uint16_t_le(depth);
170}
171
172uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
173{
174 return uint32_t_le2host(header->generation);
175}
176
177void ext4_extent_header_set_generation(ext4_extent_header_t *header,
178 uint32_t generation)
179{
180 header->generation = host2uint32_t_le(generation);
181}
182
183/**
184 * Binary search in extent index node
185 */
186static void ext4_extent_binsearch_idx(ext4_extent_header_t *header,
187 ext4_extent_index_t **index, uint32_t iblock)
188{
189 ext4_extent_index_t *r, *l, *m;
190
191 uint16_t entries_count = ext4_extent_header_get_entries_count(header);
192
193 if (entries_count == 1) {
194 *index = EXT4_EXTENT_FIRST_INDEX(header);
195 return;
196 }
197
198 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
199 r = l + entries_count - 1;
200
201 while (l <= r) {
202 m = l + (r - l) / 2;
203 uint32_t first_block = ext4_extent_index_get_first_block(m);
204 if (iblock < first_block) {
205 r = m - 1;
206 } else {
207 l = m + 1;
208 }
209 }
210
211 *index = l - 1;
212}
213
214/**
215 * Binary search in extent leaf node
216 */
217static void ext4_extent_binsearch(ext4_extent_header_t *header,
218 ext4_extent_t **extent, uint32_t iblock)
219{
220 ext4_extent_t *r, *l, *m;
221
222 uint16_t entries_count = ext4_extent_header_get_entries_count(header);
223
224 if (entries_count == 0) {
225 // this leaf is empty
226// EXT4FS_DBG("EMPTY LEAF");
227 *extent = NULL;
228 return;
229 }
230
231 if (entries_count == 1) {
232 *extent = EXT4_EXTENT_FIRST(header);
233 return;
234 }
235
236 l = EXT4_EXTENT_FIRST(header) + 1;
237 r = l + entries_count - 1;
238
239 while (l < r) {
240 m = l + (r - l) / 2;
241 uint32_t first_block = ext4_extent_get_first_block(m);
242 if (iblock < first_block) {
243 r = m - 1;
244 } else {
245 l = m + 1;
246 }
247 }
248
249 *extent = l - 1;
250}
251
252// Reading routine without saving blocks to path - for saving memory during finding block
253int ext4_extent_find_block(ext4_inode_ref_t *inode_ref, uint32_t iblock, uint32_t *fblock)
254{
255 int rc;
256
257 uint64_t inode_size = ext4_inode_get_size(
258 inode_ref->fs->superblock, inode_ref->inode);
259
260 uint32_t block_size = ext4_superblock_get_block_size(
261 inode_ref->fs->superblock);
262
263 uint32_t last_idx = (inode_size - 1) / block_size;
264
265 if (iblock > last_idx) {
266 *fblock = 0;
267 return EOK;
268 }
269
270 block_t* block = NULL;
271
272 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode_ref->inode);
273 while (ext4_extent_header_get_depth(header) != 0) {
274
275 ext4_extent_index_t *index;
276 ext4_extent_binsearch_idx(header, &index, iblock);
277
278 uint64_t child = ext4_extent_index_get_leaf(index);
279
280 if (block != NULL) {
281 block_put(block);
282 }
283
284 rc = block_get(&block, inode_ref->fs->device, child, BLOCK_FLAGS_NONE);
285 if (rc != EOK) {
286 return rc;
287 }
288
289 header = (ext4_extent_header_t *)block->data;
290 }
291
292
293 ext4_extent_t* extent = NULL;
294 ext4_extent_binsearch(header, &extent, iblock);
295
296 if (extent == NULL) {
297 *fblock = 0;
298 } else {
299 uint32_t phys_block;
300 phys_block = ext4_extent_get_start(extent) + iblock;
301 phys_block -= ext4_extent_get_first_block(extent);
302
303 *fblock = phys_block;
304 }
305
306 if (block != NULL) {
307 block_put(block);
308 }
309
310 return EOK;
311}
312
313static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref,
314 uint32_t iblock, ext4_extent_path_t **ret_path)
315{
316 int rc;
317
318 ext4_extent_header_t *eh =
319 ext4_inode_get_extent_header(inode_ref->inode);
320
321 uint16_t depth = ext4_extent_header_get_depth(eh);
322
323 ext4_extent_path_t *tmp_path;
324
325 // Added 2 for possible tree growing
326 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
327 if (tmp_path == NULL) {
328 return ENOMEM;
329 }
330
331 tmp_path[0].block = inode_ref->block;
332 tmp_path[0].header = eh;
333
334 uint16_t pos = 0;
335 while (ext4_extent_header_get_depth(eh) != 0) {
336
337 ext4_extent_binsearch_idx(tmp_path[pos].header, &tmp_path[pos].index, iblock);
338
339 tmp_path[pos].depth = depth;
340 tmp_path[pos].extent = NULL;
341
342 assert(tmp_path[pos].index != NULL);
343
344 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
345
346 block_t *block;
347 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
348 if (rc != EOK) {
349 goto cleanup;
350 }
351
352 pos++;
353
354 eh = (ext4_extent_header_t *)block->data;
355 tmp_path[pos].block = block;
356 tmp_path[pos].header = eh;
357
358 }
359
360 tmp_path[pos].depth = 0;
361 tmp_path[pos].extent = NULL;
362 tmp_path[pos].index = NULL;
363
364 /* find extent */
365 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
366
367 *ret_path = tmp_path;
368
369 return EOK;
370
371cleanup:
372 // Put loaded blocks
373 // From 1 -> 0 is a block with inode data
374 for (uint16_t i = 1; i < tmp_path->depth; ++i) {
375 if (tmp_path[i].block) {
376 block_put(tmp_path[i].block);
377 }
378 }
379
380 // Destroy temporary data structure
381 free(tmp_path);
382
383 return rc;
384
385}
386
387static int ext4_extent_release(ext4_inode_ref_t *inode_ref, ext4_extent_t* extent)
388{
389 int rc;
390
391 uint64_t start = ext4_extent_get_start(extent);
392 uint16_t block_count = ext4_extent_get_block_count(extent);
393
394 rc = ext4_balloc_free_blocks(inode_ref, start, block_count);
395 if (rc != EOK) {
396 EXT4FS_DBG("ERROR");
397 return rc;
398 }
399
400 return EOK;
401}
402
403// Recursive release
404static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
405 ext4_extent_index_t *index)
406{
407 int rc;
408
409 block_t* block;
410
411 uint32_t fblock = ext4_extent_index_get_leaf(index);
412
413// EXT4FS_DBG("fblock = \%u", fblock);
414
415 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
416 if (rc != EOK) {
417 EXT4FS_DBG("ERROR get_block");
418 return rc;
419 }
420
421 ext4_extent_header_t *header = block->data;
422
423 if (ext4_extent_header_get_depth(header)) {
424
425 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
426
427 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++idx) {
428 rc = ext4_extent_release_branch(inode_ref, idx);
429 if (rc != EOK) {
430 EXT4FS_DBG("error recursion");
431 return rc;
432 }
433 }
434 } else {
435 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
436
437 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++ext) {
438 rc = ext4_extent_release(inode_ref, ext);
439 if (rc != EOK) {
440 EXT4FS_DBG("error recursion");
441 return rc;
442 }
443 }
444 }
445
446 rc = block_put(block);
447 if (rc != EOK) {
448 EXT4FS_DBG("ERROR put_block");
449 return rc;
450 }
451
452 ext4_balloc_free_block(inode_ref, fblock);
453
454 return EOK;
455}
456
457int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
458 uint32_t iblock_from)
459{
460 int rc = EOK;
461
462 // Find the first extent to modify
463 ext4_extent_path_t *path;
464 rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
465 if (rc != EOK) {
466 return rc;
467 }
468
469 // Jump to last item of the path (extent)
470 ext4_extent_path_t *path_ptr = path;
471 while (path_ptr->depth != 0) {
472 path_ptr++;
473 }
474
475 assert(path_ptr->extent != NULL);
476
477 // First extent maybe released partially
478 uint32_t first_fblock;
479 first_fblock = ext4_extent_get_start(path_ptr->extent) + iblock_from;
480 first_fblock -= ext4_extent_get_first_block(path_ptr->extent);
481
482 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
483
484 uint16_t delete_count = block_count - first_fblock +
485 ext4_extent_get_start(path_ptr->extent);
486
487 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
488 if (rc != EOK) {
489 goto cleanup;
490 }
491
492 block_count -= delete_count;
493 ext4_extent_set_block_count(path_ptr->extent, block_count);
494
495 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
496 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
497 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
498
499 // If first extent empty, release it
500 if (block_count == 0) {
501 entries--;
502 ext4_extent_header_set_entries_count(path_ptr->header, entries);
503 }
504
505 // Release all successors of the first extent in the same node
506 while (tmp_ext < stop_ext) {
507 first_fblock = ext4_extent_get_start(tmp_ext);
508 delete_count = ext4_extent_get_block_count(tmp_ext);
509
510 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
511 if (rc != EOK) {
512 goto cleanup;
513 }
514
515 entries--;
516 ext4_extent_header_set_entries_count(path_ptr->header, entries);
517
518 tmp_ext++;
519 }
520
521 // If leaf node is empty, the whole tree must be checked and the node will be released
522 bool check_tree = false;
523
524 // Don't release root block (including inode data) !!!
525 if ((path_ptr != path) && (entries == 0)) {
526 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
527 if (rc != EOK) {
528 goto cleanup;
529 }
530 check_tree = true;
531 }
532
533 // Jump to the parent
534 --path_ptr;
535
536 // release all successors in all levels
537 while(path_ptr >= path) {
538 entries = ext4_extent_header_get_entries_count(path_ptr->header);
539 ext4_extent_index_t *index = path_ptr->index + 1;
540 ext4_extent_index_t *stop =
541 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
542
543 if (check_tree) {
544 entries--;
545 ext4_extent_header_set_entries_count(path_ptr->header, entries);
546 }
547
548 while (index < stop) {
549 rc = ext4_extent_release_branch(inode_ref, index);
550 if (rc != EOK) {
551 goto cleanup;
552 }
553 ++index;
554 --entries;
555 ext4_extent_header_set_entries_count(path_ptr->header, entries);
556 }
557
558 path_ptr->block->dirty = true;
559
560 if ((entries == 0) && (path_ptr != path)) {
561 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
562 if (rc != EOK) {
563 goto cleanup;
564 }
565 check_tree = true;
566 } else {
567 check_tree = false;
568 }
569
570 --path_ptr;
571 }
572
573
574cleanup:
575 // Put loaded blocks
576 // From 1 -> 0 is a block with inode data
577 for (uint16_t i = 1; i <= path->depth; ++i) {
578 if (path[i].block) {
579 block_put(path[i].block);
580 }
581 }
582
583 // Destroy temporary data structure
584 free(path);
585
586 return rc;
587}
588
589static int ext4_extent_append_extent(ext4_inode_ref_t *inode_ref,
590 ext4_extent_path_t *path, ext4_extent_path_t **last_path_item,
591 uint32_t iblock)
592{
593 EXT4FS_DBG("");
594 int rc;
595
596 ext4_extent_path_t *path_ptr = *last_path_item;
597
598 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
599 uint16_t limit = ext4_extent_header_get_max_entries_count(path_ptr->header);
600
601 // Trivial way - no splitting
602 if (entries < limit) {
603 EXT4FS_DBG("adding extent entry");
604
605 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
606 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
607 ext4_extent_set_block_count(path_ptr->extent, 0);
608 ext4_extent_set_first_block(path_ptr->extent, iblock);
609 ext4_extent_set_start(path_ptr->extent, 0);
610 path_ptr->block->dirty = true;
611
612 return EOK;
613 }
614
615 uint32_t block_size =
616 ext4_superblock_get_block_size(inode_ref->fs->superblock);
617
618 // Trivial tree - grow (extents were in root node)
619 if (path_ptr == path) {
620
621 uint32_t new_fblock;
622 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
623 if (rc != EOK) {
624 EXT4FS_DBG("error in block allocation");
625 return rc;
626 }
627
628 block_t *block;
629 rc = block_get(&block, inode_ref->fs->device,
630 new_fblock, BLOCK_FLAGS_NOREAD);
631 if (rc != EOK) {
632 EXT4FS_DBG("error in block_get");
633 return rc;
634 }
635
636 memset(block->data, 0, block_size);
637
638 // Move data from root to the new block
639 memcpy(block->data, inode_ref->inode->blocks,
640 EXT4_INODE_BLOCKS * sizeof(uint32_t));
641
642 path_ptr++;
643 path_ptr->block = block;
644 path_ptr->header = (ext4_extent_header_t *)block->data;
645 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
646 path_ptr->index = NULL;
647
648 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
649 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
650 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
651 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
652 sizeof(ext4_extent_t);
653 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
654
655 // Modify root (in inode)
656 path->depth = 1;
657 path->extent = NULL;
658 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
659
660 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
661 ext4_extent_header_set_entries_count(path->header, 1);
662
663 ext4_extent_index_set_first_block(path->index, 0);
664 ext4_extent_index_set_leaf(path->index, new_fblock);
665
666 path_ptr->block->dirty = true;
667 path->block->dirty = true;
668
669 *last_path_item = path_ptr;
670
671 return EOK;
672 }
673
674 assert(false);
675
676 // start splitting
677 uint32_t fblock = 0;
678 while (path_ptr > path) {
679
680 // TODO
681
682 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
683 if (rc != EOK) {
684 return rc;
685 }
686
687 block_t *block;
688 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
689 if (rc != EOK) {
690 ext4_balloc_free_block(inode_ref, fblock);
691 return rc;
692 }
693
694 // Init block
695 memset(block->data, 0, block_size);
696
697 // Not modified
698 block_put(path_ptr->block);
699 path_ptr->block = block;
700
701 path_ptr->header = block->data;
702
703 if (path_ptr->depth) {
704 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
705 } else {
706 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
707 }
708
709 path_ptr--;
710 }
711
712 // If splitting reached root node
713 if (path_ptr == path) {
714
715 uint32_t new_fblock;
716 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
717 if (rc != EOK) {
718 EXT4FS_DBG("error in block allocation");
719 return rc;
720 }
721
722 block_t *block;
723 rc = block_get(&block, inode_ref->fs->device,
724 new_fblock, BLOCK_FLAGS_NOREAD);
725 if (rc != EOK) {
726 EXT4FS_DBG("error in block_get");
727 return rc;
728 }
729
730 memset(block->data, 0, block_size);
731
732 // Move data from root to the new block
733 memcpy(block->data, inode_ref->inode->blocks,
734 EXT4_INODE_BLOCKS * sizeof(uint32_t));
735
736 path_ptr++;
737 path_ptr->block = block;
738 path_ptr->header = (ext4_extent_header_t *)block->data;
739 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
740 path_ptr->index = NULL;
741
742 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
743 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
744 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
745 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
746 sizeof(ext4_extent_t);
747 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
748
749 // Modify root (in inode)
750 path->depth = 1;
751 path->extent = NULL;
752 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
753
754 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
755 ext4_extent_header_set_entries_count(path->header, 1);
756
757 ext4_extent_index_set_first_block(path->index, 0);
758 ext4_extent_index_set_leaf(path->index, new_fblock);
759
760 path_ptr->block->dirty = true;
761 path->block->dirty = true;
762
763 *last_path_item = path_ptr;
764
765 return EOK;
766 }
767
768 return EOK;
769}
770
771int ext4_extent_append_block(ext4_inode_ref_t *inode_ref,
772 uint32_t *iblock, uint32_t *fblock)
773{
774 EXT4FS_DBG("");
775 int rc = EOK;
776
777 ext4_superblock_t *sb = inode_ref->fs->superblock;
778 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
779 uint32_t block_size = ext4_superblock_get_block_size(sb);
780
781 // Calculate number of new logical block
782 uint32_t new_block_idx = 0;
783 if (inode_size > 0) {
784 if ((inode_size % block_size) != 0) {
785 inode_size += block_size - (inode_size % block_size);
786 }
787 new_block_idx = inode_size / block_size;
788 }
789
790 // Load the nearest leaf (with extent)
791 ext4_extent_path_t *path;
792 rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
793 if (rc != EOK) {
794 return rc;
795 }
796
797 // Jump to last item of the path (extent)
798 ext4_extent_path_t *path_ptr = path;
799 while (path_ptr->depth != 0) {
800 path_ptr++;
801 }
802
803 // Add new extent to the node
804 if (path_ptr->extent == NULL) {
805 goto append_extent;
806 }
807
808 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
809 uint16_t block_limit = (1 << 15);
810
811 uint32_t phys_block = 0;
812 if (block_count < block_limit) {
813
814 if (block_count == 0) {
815
816 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
817 if (rc != EOK) {
818 goto finish;
819 }
820
821 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
822 ext4_extent_set_start(path_ptr->extent, phys_block);
823 ext4_extent_set_block_count(path_ptr->extent, 1);
824
825 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
826 inode_ref->dirty = true;
827
828 path_ptr->block->dirty = true;
829
830 goto finish;
831 } else {
832
833 phys_block = ext4_extent_get_start(path_ptr->extent);
834 phys_block += ext4_extent_get_block_count(path_ptr->extent);
835
836 bool free;
837 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
838 if (rc != EOK) {
839 goto finish;
840 }
841
842 if (! free) {
843 // target is not free
844 goto append_extent;
845 }
846
847
848 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
849
850 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
851 inode_ref->dirty = true;
852
853 path_ptr->block->dirty = true;
854
855 goto finish;
856 }
857 }
858
859append_extent:
860
861 phys_block = 0;
862 // Allocate and insert insert new block
863 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
864 if (rc != EOK) {
865 EXT4FS_DBG("error in block allocation, rc = \%d", rc);
866 goto finish;
867 }
868
869 rc = ext4_extent_append_extent(inode_ref, path, &path_ptr, new_block_idx);
870 if (rc != EOK) {
871 ext4_balloc_free_block(inode_ref, phys_block);
872 goto finish;
873 }
874
875 ext4_extent_set_block_count(path_ptr->extent, 1);
876 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
877 ext4_extent_set_start(path_ptr->extent, phys_block);
878
879 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
880 inode_ref->dirty = true;
881
882 path_ptr->block->dirty = true;
883
884finish:
885
886 *iblock = new_block_idx;
887 *fblock = phys_block;
888
889 // Put loaded blocks
890 // From 1 -> 0 is a block with inode data
891 for (uint16_t i = 1; i <= path->depth; ++i) {
892 if (path[i].block) {
893 block_put(path[i].block);
894 }
895 }
896
897 // Destroy temporary data structure
898 free(path);
899
900 return rc;
901}
902
903/**
904 * @}
905 */
Note: See TracBrowser for help on using the repository browser.