source: mainline/uspace/lib/ext4/libext4_extent.c@ 728d771

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

next part of comments on extent code

  • Property mode set to 100644
File size: 26.0 KB
RevLine 
[829d238]1/*
[f22d5ef0]2 * Copyright (c) 2012 Frantisek Princ
[829d238]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
[c25e39b]35 * @brief Ext4 extent structures operations.
[829d238]36 */
37
[acd869e]38#include <byteorder.h>
[e2629b08]39#include <errno.h>
40#include <malloc.h>
41#include "libext4.h"
[829d238]42
[728d771]43/** Get logical number of the block covered by extent.
[cb7056a]44 *
45 * @param extent extent to load number from
46 * @return logical number of the first block covered by extent
47 */
[8958a26]48uint32_t ext4_extent_get_first_block(ext4_extent_t *extent)
49{
50 return uint32_t_le2host(extent->first_block);
51}
52
[cb7056a]53/** Set logical number of the first block covered by extent.
54 *
[728d771]55 * @param extent extent to set number to
[cb7056a]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)
[343ccfd]59{
[cb7056a]60 extent->first_block = host2uint32_t_le(iblock);
[343ccfd]61}
62
[cb7056a]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 */
[8958a26]68uint16_t ext4_extent_get_block_count(ext4_extent_t *extent)
69{
70 return uint16_t_le2host(extent->block_count);
71}
72
[cb7056a]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)
[343ccfd]79{
[cb7056a]80 extent->block_count = host2uint16_t_le(count);
[343ccfd]81}
82
[cb7056a]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 */
[8958a26]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));
[343ccfd]92}
[8958a26]93
[cb7056a]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)
[343ccfd]100{
[cb7056a]101 extent->start_lo = host2uint32_t_le((fblock << 32) >> 32);
102 extent->start_hi = host2uint16_t_le((uint16_t)(fblock >> 32));
[8958a26]103}
104
[728d771]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 */
[1a7756a]110uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
111{
112 return uint32_t_le2host(index->first_block);
113}
114
[728d771]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 */
[343ccfd]120void ext4_extent_index_set_first_block(ext4_extent_index_t *index,
[728d771]121 uint32_t iblock)
[343ccfd]122{
[728d771]123 index->first_block = host2uint32_t_le(iblock);
[343ccfd]124}
125
[728d771]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 */
[1a7756a]131uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
132{
133 return ((uint64_t)uint16_t_le2host(index->leaf_hi)) << 32 |
[343ccfd]134 ((uint64_t)uint32_t_le2host(index->leaf_lo));
135}
136
[728d771]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)
[343ccfd]143{
[728d771]144 index->leaf_lo = host2uint32_t_le((fblock << 32) >> 32);
145 index->leaf_hi = host2uint16_t_le((uint16_t)(fblock >> 32));
[1a7756a]146}
147
[728d771]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 */
[acd869e]153uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
154{
155 return uint16_t_le2host(header->magic);
156}
157
[728d771]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 */
[343ccfd]163void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
164{
165 header->magic = host2uint16_t_le(magic);
166}
167
[728d771]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 */
[acd869e]173uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
174{
175 return uint16_t_le2host(header->entries_count);
176}
177
[728d771]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 */
[343ccfd]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
[728d771]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 */
[acd869e]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
[728d771]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 */
[343ccfd]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
[728d771]210/** Get depth of extent subtree.
211 *
212 * @param header extent header to get value from
213 * @return depth of extent subtree
214 */
[acd869e]215uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
216{
217 return uint16_t_le2host(header->depth);
218}
219
[728d771]220/** Set depth of extent subtree.
221 *
222 * @param header extent header to set value to
223 * @param depth depth of extent subtree
224 */
[343ccfd]225void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
226{
227 header->depth = host2uint16_t_le(depth);
228}
229
[728d771]230/** Get generation from extent header
231 *
232 * @param header extent header to get value from
233 * @return generation
234 */
[acd869e]235uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
236{
237 return uint32_t_le2host(header->generation);
238}
[829d238]239
[728d771]240/** Set generation to extent header
241 *
242 * @param header extent header to set value to
243 * @param generation generation
244 */
[343ccfd]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
[728d771]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
[fffb061]256 */
[47faec1]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
[728d771]264 // Check trivial situation
[47faec1]265 if (entries_count == 1) {
266 *index = EXT4_EXTENT_FIRST_INDEX(header);
267 return;
268 }
269
[728d771]270 // Initialize bounds
[47faec1]271 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
272 r = l + entries_count - 1;
273
[728d771]274 // Do binary search
[47faec1]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
[728d771]285 // Set output value
[47faec1]286 *index = l - 1;
287}
288
[728d771]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 *
[fffb061]295 */
[30ac3c3]296static void ext4_extent_binsearch(ext4_extent_header_t *header,
297 ext4_extent_t **extent, uint32_t iblock)
[a4419e7]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
[47faec1]305 *extent = NULL;
306 return;
307 }
308
[728d771]309 // Check trivial situation
[47faec1]310 if (entries_count == 1) {
311 *extent = EXT4_EXTENT_FIRST(header);
[a4419e7]312 return;
313 }
314
[728d771]315 // Initialize bounds
[a4419e7]316 l = EXT4_EXTENT_FIRST(header) + 1;
317 r = l + entries_count - 1;
[9104bb5]318
[728d771]319 // Do binary search
[0d4db0f]320 while (l < r) {
[a4419e7]321 m = l + (r - l) / 2;
[47faec1]322 uint32_t first_block = ext4_extent_get_first_block(m);
323 if (iblock < first_block) {
[a4419e7]324 r = m - 1;
325 } else {
326 l = m + 1;
327 }
328 }
329
[728d771]330 // Set output value
[30ac3c3]331 *extent = l - 1;
[a4419e7]332}
333
[728d771]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)
[e2629b08]345{
[9104bb5]346 int rc;
[e2629b08]347
[728d771]348 // Compute bound defined by i-node size
[b73530a]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
[728d771]357 // Check if requested iblock is not over size of i-node
[b73530a]358 if (iblock > last_idx) {
359 *fblock = 0;
360 return EOK;
361 }
362
[1ac1ab4]363 block_t* block = NULL;
[e2629b08]364
[728d771]365 // Walk through extent tree
[1ac1ab4]366 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode_ref->inode);
367 while (ext4_extent_header_get_depth(header) != 0) {
[e2629b08]368
[728d771]369 // Search index in node
[1ac1ab4]370 ext4_extent_index_t *index;
371 ext4_extent_binsearch_idx(header, &index, iblock);
[fffb061]372
[728d771]373 // Load child node and set values for the next iteration
[1ac1ab4]374 uint64_t child = ext4_extent_index_get_leaf(index);
[fffb061]375
[1ac1ab4]376 if (block != NULL) {
377 block_put(block);
378 }
[e2629b08]379
[1ac1ab4]380 rc = block_get(&block, inode_ref->fs->device, child, BLOCK_FLAGS_NONE);
[47faec1]381 if (rc != EOK) {
382 return rc;
383 }
[e2629b08]384
[1ac1ab4]385 header = (ext4_extent_header_t *)block->data;
[47faec1]386 }
[e2629b08]387
[728d771]388 // Search extent in the leaf block
[0d4db0f]389 ext4_extent_t* extent = NULL;
[1ac1ab4]390 ext4_extent_binsearch(header, &extent, iblock);
[e2629b08]391
[728d771]392 // Prevent empty leaf
[1196df6]393 if (extent == NULL) {
394 *fblock = 0;
395 } else {
[728d771]396
397 // Compute requested physical block address
[1196df6]398 uint32_t phys_block;
399 phys_block = ext4_extent_get_start(extent) + iblock;
400 phys_block -= ext4_extent_get_first_block(extent);
[e2629b08]401
[1196df6]402 *fblock = phys_block;
403 }
[e2629b08]404
[728d771]405 // Cleanup
[1ac1ab4]406 if (block != NULL) {
407 block_put(block);
[fffb061]408 }
[9104bb5]409
[fffb061]410 return EOK;
[e2629b08]411}
412
[728d771]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 */
[0d4db0f]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
[728d771]442 // Initialize structure for algorithm start
[0d4db0f]443 tmp_path[0].block = inode_ref->block;
444 tmp_path[0].header = eh;
445
[728d771]446 // Walk through the extent tree
[0d4db0f]447 uint16_t pos = 0;
448 while (ext4_extent_header_get_depth(eh) != 0) {
449
[728d771]450 // Search index in index node by iblock
[0d4db0f]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
[728d771]458 // Load information for the next iteration
[0d4db0f]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) {
[6f413125]464 goto cleanup;
[0d4db0f]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
[728d771]479 // Find extent in the leaf node
[0d4db0f]480 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
481
482 *ret_path = tmp_path;
483
484 return EOK;
[6f413125]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;
[0d4db0f]499}
500
[728d771]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)
[0d4db0f]509{
510 int rc;
511
[728d771]512 // Compute number of the first physical block to release
[5b0a3946]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) {
[728d771]518 EXT4FS_DBG("Error in releasing data blocks");
[5b0a3946]519 return rc;
520 }
521
522 return EOK;
523}
524
[728d771]525// TODO comments
526
[5b0a3946]527// Recursive release
528static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
529 ext4_extent_index_t *index)
530{
531 int rc;
532
533 block_t* block;
534
535 uint32_t fblock = ext4_extent_index_get_leaf(index);
536
[82cb6768]537// EXT4FS_DBG("fblock = \%u", fblock);
[5b0a3946]538
539 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
540 if (rc != EOK) {
541 EXT4FS_DBG("ERROR get_block");
542 return rc;
543 }
544
545 ext4_extent_header_t *header = block->data;
546
547 if (ext4_extent_header_get_depth(header)) {
548
549 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
550
551 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++idx) {
552 rc = ext4_extent_release_branch(inode_ref, idx);
553 if (rc != EOK) {
554 EXT4FS_DBG("error recursion");
555 return rc;
556 }
557 }
558 } else {
559 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
560
561 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++ext) {
562 rc = ext4_extent_release(inode_ref, ext);
563 if (rc != EOK) {
564 EXT4FS_DBG("error recursion");
565 return rc;
566 }
567 }
568 }
569
570 rc = block_put(block);
571 if (rc != EOK) {
572 EXT4FS_DBG("ERROR put_block");
573 return rc;
574 }
575
576 ext4_balloc_free_block(inode_ref, fblock);
577
578 return EOK;
579}
580
[1196df6]581int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
582 uint32_t iblock_from)
[5b0a3946]583{
[6f413125]584 int rc = EOK;
[5b0a3946]585
[3e2952b]586 // Find the first extent to modify
[0d4db0f]587 ext4_extent_path_t *path;
[5b0a3946]588 rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
[0d4db0f]589 if (rc != EOK) {
590 return rc;
591 }
592
[3e2952b]593 // Jump to last item of the path (extent)
[0d4db0f]594 ext4_extent_path_t *path_ptr = path;
595 while (path_ptr->depth != 0) {
596 path_ptr++;
597 }
598
599 assert(path_ptr->extent != NULL);
600
[3e2952b]601 // First extent maybe released partially
[5b0a3946]602 uint32_t first_fblock;
603 first_fblock = ext4_extent_get_start(path_ptr->extent) + iblock_from;
604 first_fblock -= ext4_extent_get_first_block(path_ptr->extent);
[0d4db0f]605
606 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
607
[5b0a3946]608 uint16_t delete_count = block_count - first_fblock +
609 ext4_extent_get_start(path_ptr->extent);
610
611 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
[3e2952b]612 if (rc != EOK) {
[6f413125]613 goto cleanup;
[3e2952b]614 }
[0d4db0f]615
[5b0a3946]616 block_count -= delete_count;
[0d4db0f]617 ext4_extent_set_block_count(path_ptr->extent, block_count);
618
[3e2952b]619 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
620 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
621 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
[5b0a3946]622
[3e2952b]623 // If first extent empty, release it
[0d4db0f]624 if (block_count == 0) {
625 entries--;
626 ext4_extent_header_set_entries_count(path_ptr->header, entries);
627 }
628
[3e2952b]629 // Release all successors of the first extent in the same node
630 while (tmp_ext < stop_ext) {
631 first_fblock = ext4_extent_get_start(tmp_ext);
632 delete_count = ext4_extent_get_block_count(tmp_ext);
[5b0a3946]633
[3e2952b]634 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
635 if (rc != EOK) {
[6f413125]636 goto cleanup;
[3e2952b]637 }
[5b0a3946]638
[3e2952b]639 entries--;
640 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[5b0a3946]641
[3e2952b]642 tmp_ext++;
643 }
[5b0a3946]644
[3e2952b]645 // If leaf node is empty, the whole tree must be checked and the node will be released
646 bool check_tree = false;
[5b0a3946]647
[3e2952b]648 // Don't release root block (including inode data) !!!
649 if ((path_ptr != path) && (entries == 0)) {
650 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
651 if (rc != EOK) {
[6f413125]652 goto cleanup;
[001307cf]653 }
[3e2952b]654 check_tree = true;
[5b0a3946]655 }
[001307cf]656
[3e2952b]657 // Jump to the parent
658 --path_ptr;
[5b0a3946]659
[3e2952b]660 // release all successors in all levels
661 while(path_ptr >= path) {
662 entries = ext4_extent_header_get_entries_count(path_ptr->header);
663 ext4_extent_index_t *index = path_ptr->index + 1;
664 ext4_extent_index_t *stop =
665 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
[001307cf]666
[3e2952b]667 if (check_tree) {
668 entries--;
669 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[001307cf]670 }
[0d4db0f]671
[5b0a3946]672 while (index < stop) {
673 rc = ext4_extent_release_branch(inode_ref, index);
674 if (rc != EOK) {
[6f413125]675 goto cleanup;
[5b0a3946]676 }
677 ++index;
[3e2952b]678 --entries;
679 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[5b0a3946]680 }
681
[3e2952b]682 path_ptr->block->dirty = true;
[5b0a3946]683
[3e2952b]684 if ((entries == 0) && (path_ptr != path)) {
685 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
[5b0a3946]686 if (rc != EOK) {
[6f413125]687 goto cleanup;
[5b0a3946]688 }
[3e2952b]689 check_tree = true;
690 } else {
691 check_tree = false;
[5b0a3946]692 }
[3e2952b]693
694 --path_ptr;
[0d4db0f]695 }
696
[3e2952b]697
[6f413125]698cleanup:
[0d4db0f]699 // Put loaded blocks
700 // From 1 -> 0 is a block with inode data
[82cb6768]701 for (uint16_t i = 1; i <= path->depth; ++i) {
[0d4db0f]702 if (path[i].block) {
703 block_put(path[i].block);
704 }
705 }
706
707 // Destroy temporary data structure
708 free(path);
709
[6f413125]710 return rc;
[0d4db0f]711}
[1ac1ab4]712
[82cb6768]713static int ext4_extent_append_extent(ext4_inode_ref_t *inode_ref,
714 ext4_extent_path_t *path, ext4_extent_path_t **last_path_item,
715 uint32_t iblock)
716{
[7eb033ce]717 EXT4FS_DBG("");
[82cb6768]718 int rc;
719
720 ext4_extent_path_t *path_ptr = *last_path_item;
721
722 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
723 uint16_t limit = ext4_extent_header_get_max_entries_count(path_ptr->header);
724
725 // Trivial way - no splitting
726 if (entries < limit) {
[7eb033ce]727 EXT4FS_DBG("adding extent entry");
728
[82cb6768]729 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
730 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
731 ext4_extent_set_block_count(path_ptr->extent, 0);
732 ext4_extent_set_first_block(path_ptr->extent, iblock);
733 ext4_extent_set_start(path_ptr->extent, 0);
734 path_ptr->block->dirty = true;
735
736 return EOK;
737 }
738
739 uint32_t block_size =
740 ext4_superblock_get_block_size(inode_ref->fs->superblock);
741
[7eb033ce]742 // Trivial tree - grow (extents were in root node)
[82cb6768]743 if (path_ptr == path) {
744
745 uint32_t new_fblock;
746 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
747 if (rc != EOK) {
748 EXT4FS_DBG("error in block allocation");
749 return rc;
750 }
751
752 block_t *block;
753 rc = block_get(&block, inode_ref->fs->device,
754 new_fblock, BLOCK_FLAGS_NOREAD);
755 if (rc != EOK) {
756 EXT4FS_DBG("error in block_get");
757 return rc;
758 }
759
760 memset(block->data, 0, block_size);
761
762 // Move data from root to the new block
763 memcpy(block->data, inode_ref->inode->blocks,
764 EXT4_INODE_BLOCKS * sizeof(uint32_t));
765
766 path_ptr++;
767 path_ptr->block = block;
768 path_ptr->header = (ext4_extent_header_t *)block->data;
769 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
770 path_ptr->index = NULL;
771
772 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
773 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
774 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
775 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
776 sizeof(ext4_extent_t);
777 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
778
779 // Modify root (in inode)
780 path->depth = 1;
781 path->extent = NULL;
782 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
783
784 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
785 ext4_extent_header_set_entries_count(path->header, 1);
786
787 ext4_extent_index_set_first_block(path->index, 0);
788 ext4_extent_index_set_leaf(path->index, new_fblock);
789
790 path_ptr->block->dirty = true;
791 path->block->dirty = true;
792
793 *last_path_item = path_ptr;
794
795 return EOK;
796 }
797
[7eb033ce]798 assert(false);
799
[82cb6768]800 // start splitting
801 uint32_t fblock = 0;
802 while (path_ptr > path) {
803
[7eb033ce]804 // TODO
805
[82cb6768]806 rc = ext4_balloc_alloc_block(inode_ref, &fblock);
807 if (rc != EOK) {
808 return rc;
809 }
810
811 block_t *block;
812 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
813 if (rc != EOK) {
814 ext4_balloc_free_block(inode_ref, fblock);
815 return rc;
816 }
817
818 // Init block
819 memset(block->data, 0, block_size);
820
821 // Not modified
822 block_put(path_ptr->block);
823 path_ptr->block = block;
824
825 path_ptr->header = block->data;
826
827 if (path_ptr->depth) {
828 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
829 } else {
830 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
831 }
[7eb033ce]832
833 path_ptr--;
834 }
835
836 // If splitting reached root node
837 if (path_ptr == path) {
838
839 uint32_t new_fblock;
840 rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
841 if (rc != EOK) {
842 EXT4FS_DBG("error in block allocation");
843 return rc;
844 }
845
846 block_t *block;
847 rc = block_get(&block, inode_ref->fs->device,
848 new_fblock, BLOCK_FLAGS_NOREAD);
849 if (rc != EOK) {
850 EXT4FS_DBG("error in block_get");
851 return rc;
852 }
853
854 memset(block->data, 0, block_size);
855
856 // Move data from root to the new block
857 memcpy(block->data, inode_ref->inode->blocks,
858 EXT4_INODE_BLOCKS * sizeof(uint32_t));
859
860 path_ptr++;
861 path_ptr->block = block;
862 path_ptr->header = (ext4_extent_header_t *)block->data;
863 path_ptr->depth = ext4_extent_header_get_depth(path_ptr->header);
864 path_ptr->index = NULL;
865
866 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
867 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
868 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
869 uint16_t limit = (block_size - sizeof(ext4_extent_header_t)) /
870 sizeof(ext4_extent_t);
871 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
872
873 // Modify root (in inode)
874 path->depth = 1;
875 path->extent = NULL;
876 path->index = EXT4_EXTENT_FIRST_INDEX(path->header);
877
878 ext4_extent_header_set_depth(path->header, path_ptr->depth + 1);
879 ext4_extent_header_set_entries_count(path->header, 1);
880
881 ext4_extent_index_set_first_block(path->index, 0);
882 ext4_extent_index_set_leaf(path->index, new_fblock);
883
884 path_ptr->block->dirty = true;
885 path->block->dirty = true;
886
887 *last_path_item = path_ptr;
888
889 return EOK;
[82cb6768]890 }
891
892 return EOK;
893}
894
[ce6de59]895int ext4_extent_append_block(ext4_inode_ref_t *inode_ref,
896 uint32_t *iblock, uint32_t *fblock)
897{
[7eb033ce]898 EXT4FS_DBG("");
[6f413125]899 int rc = EOK;
[ce6de59]900
901 ext4_superblock_t *sb = inode_ref->fs->superblock;
902 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
903 uint32_t block_size = ext4_superblock_get_block_size(sb);
[b73530a]904
[82cb6768]905 // Calculate number of new logical block
[b73530a]906 uint32_t new_block_idx = 0;
907 if (inode_size > 0) {
908 if ((inode_size % block_size) != 0) {
909 inode_size += block_size - (inode_size % block_size);
910 }
911 new_block_idx = inode_size / block_size;
912 }
[ce6de59]913
[82cb6768]914 // Load the nearest leaf (with extent)
[ce6de59]915 ext4_extent_path_t *path;
916 rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
917 if (rc != EOK) {
918 return rc;
919 }
920
921 // Jump to last item of the path (extent)
922 ext4_extent_path_t *path_ptr = path;
923 while (path_ptr->depth != 0) {
924 path_ptr++;
925 }
926
[82cb6768]927 // Add new extent to the node
928 if (path_ptr->extent == NULL) {
929 goto append_extent;
930 }
[936132f]931
[82cb6768]932 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
933 uint16_t block_limit = (1 << 15);
[936132f]934
[b6d7b7c]935 uint32_t phys_block = 0;
[82cb6768]936 if (block_count < block_limit) {
[ce6de59]937
[82cb6768]938 if (block_count == 0) {
[ce6de59]939
[82cb6768]940 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
941 if (rc != EOK) {
[7eb033ce]942 goto finish;
[82cb6768]943 }
[ce6de59]944
[82cb6768]945 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
946 ext4_extent_set_start(path_ptr->extent, phys_block);
947 ext4_extent_set_block_count(path_ptr->extent, 1);
[b73530a]948
[b6d7b7c]949 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
950 inode_ref->dirty = true;
951
[82cb6768]952 path_ptr->block->dirty = true;
[ce6de59]953
[6f413125]954 goto finish;
[82cb6768]955 } else {
[ce6de59]956
[82cb6768]957 phys_block = ext4_extent_get_start(path_ptr->extent);
958 phys_block += ext4_extent_get_block_count(path_ptr->extent);
[ce6de59]959
[82cb6768]960 bool free;
961 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
962 if (rc != EOK) {
[7eb033ce]963 goto finish;
[82cb6768]964 }
[ce6de59]965
[82cb6768]966 if (! free) {
967 // target is not free
968 goto append_extent;
969 }
[ce6de59]970
[b73530a]971
[82cb6768]972 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
[b73530a]973
[b6d7b7c]974 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
975 inode_ref->dirty = true;
976
[82cb6768]977 path_ptr->block->dirty = true;
[b73530a]978
[82cb6768]979 goto finish;
980 }
981 }
[b73530a]982
[82cb6768]983append_extent:
[ce6de59]984
[82cb6768]985 phys_block = 0;
986 // Allocate and insert insert new block
987 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
988 if (rc != EOK) {
989 EXT4FS_DBG("error in block allocation, rc = \%d", rc);
990 goto finish;
991 }
[b73530a]992
[82cb6768]993 rc = ext4_extent_append_extent(inode_ref, path, &path_ptr, new_block_idx);
994 if (rc != EOK) {
995 ext4_balloc_free_block(inode_ref, phys_block);
996 goto finish;
[ce6de59]997 }
998
[82cb6768]999 ext4_extent_set_block_count(path_ptr->extent, 1);
1000 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1001 ext4_extent_set_start(path_ptr->extent, phys_block);
1002
[b6d7b7c]1003 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1004 inode_ref->dirty = true;
1005
[82cb6768]1006 path_ptr->block->dirty = true;
[ce6de59]1007
1008finish:
[1196df6]1009
1010 *iblock = new_block_idx;
1011 *fblock = phys_block;
1012
[ce6de59]1013 // Put loaded blocks
1014 // From 1 -> 0 is a block with inode data
[82cb6768]1015 for (uint16_t i = 1; i <= path->depth; ++i) {
[ce6de59]1016 if (path[i].block) {
1017 block_put(path[i].block);
1018 }
1019 }
1020
1021 // Destroy temporary data structure
1022 free(path);
1023
[6f413125]1024 return rc;
[ce6de59]1025}
1026
[829d238]1027/**
1028 * @}
1029 */
Note: See TracBrowser for help on using the repository browser.