source: mainline/uspace/lib/ext4/libext4_extent.c@ 8eff2b0

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

redundant condition removed

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