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

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

Copyright update

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