source: mainline/uspace/lib/ext4/libext4_extent.c@ 9a487cc

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

fixed bug in algorithm for delete big files

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