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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bd5d4e1 was bd5d4e1, checked in by Maurizio Lombardi <m.lombardi85@…>, 10 years ago

libex4: fix block leak in error code path.

  • Property mode set to 100644
File size: 29.8 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 * @{
[38542dc]31 */
[829d238]32/**
[38542dc]33 * @file libext4_extent.c
34 * @brief Ext4 extent structures operations.
[829d238]35 */
36
[acd869e]37#include <byteorder.h>
[e2629b08]38#include <errno.h>
39#include <malloc.h>
40#include "libext4.h"
[829d238]41
[728d771]42/** Get logical number of the block covered by extent.
[cb7056a]43 *
[38542dc]44 * @param extent Extent to load number from
45 *
46 * @return Logical number of the first block covered by extent
47 *
[cb7056a]48 */
[8958a26]49uint32_t ext4_extent_get_first_block(ext4_extent_t *extent)
50{
51 return uint32_t_le2host(extent->first_block);
52}
53
[cb7056a]54/** Set logical number of the first block covered by extent.
55 *
[38542dc]56 * @param extent Extent to set number to
57 * @param iblock Logical number of the first block covered by extent
58 *
[cb7056a]59 */
60void ext4_extent_set_first_block(ext4_extent_t *extent, uint32_t iblock)
[343ccfd]61{
[cb7056a]62 extent->first_block = host2uint32_t_le(iblock);
[343ccfd]63}
64
[cb7056a]65/** Get number of blocks covered by extent.
66 *
[38542dc]67 * @param extent Extent to load count from
68 *
69 * @return Number of blocks covered by extent
70 *
[cb7056a]71 */
[8958a26]72uint16_t ext4_extent_get_block_count(ext4_extent_t *extent)
73{
74 return uint16_t_le2host(extent->block_count);
75}
76
[cb7056a]77/** Set number of blocks covered by extent.
78 *
[38542dc]79 * @param extent Extent to load count from
80 * @param count Number of blocks covered by extent
81 *
[cb7056a]82 */
83void ext4_extent_set_block_count(ext4_extent_t *extent, uint16_t count)
[343ccfd]84{
[cb7056a]85 extent->block_count = host2uint16_t_le(count);
[343ccfd]86}
87
[cb7056a]88/** Get physical number of the first block covered by extent.
89 *
[38542dc]90 * @param extent Extent to load number
91 *
92 * @return Physical number of the first block covered by extent
93 *
[cb7056a]94 */
[8958a26]95uint64_t ext4_extent_get_start(ext4_extent_t *extent)
96{
97 return ((uint64_t)uint16_t_le2host(extent->start_hi)) << 32 |
[38542dc]98 ((uint64_t)uint32_t_le2host(extent->start_lo));
[343ccfd]99}
[8958a26]100
[cb7056a]101/** Set physical number of the first block covered by extent.
102 *
[38542dc]103 * @param extent Extent to load number
104 * @param fblock Physical number of the first block covered by extent
105 *
[cb7056a]106 */
107void ext4_extent_set_start(ext4_extent_t *extent, uint64_t fblock)
[343ccfd]108{
[cb7056a]109 extent->start_lo = host2uint32_t_le((fblock << 32) >> 32);
110 extent->start_hi = host2uint16_t_le((uint16_t)(fblock >> 32));
[8958a26]111}
112
[728d771]113/** Get logical number of the block covered by extent index.
114 *
[38542dc]115 * @param index Extent index to load number from
116 *
117 * @return Logical number of the first block covered by extent index
118 *
[728d771]119 */
[1a7756a]120uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
121{
122 return uint32_t_le2host(index->first_block);
123}
124
[728d771]125/** Set logical number of the block covered by extent index.
126 *
[38542dc]127 * @param index Extent index to set number to
128 * @param iblock Logical number of the first block covered by extent index
129 *
[728d771]130 */
[343ccfd]131void ext4_extent_index_set_first_block(ext4_extent_index_t *index,
[38542dc]132 uint32_t iblock)
[343ccfd]133{
[728d771]134 index->first_block = host2uint32_t_le(iblock);
[343ccfd]135}
136
[728d771]137/** Get physical number of block where the child node is located.
138 *
[38542dc]139 * @param index Extent index to load number from
140 *
141 * @return Physical number of the block with child node
142 *
[728d771]143 */
[1a7756a]144uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
145{
[38542dc]146 return ((uint64_t) uint16_t_le2host(index->leaf_hi)) << 32 |
147 ((uint64_t)uint32_t_le2host(index->leaf_lo));
[343ccfd]148}
149
[728d771]150/** Set physical number of block where the child node is located.
151 *
[38542dc]152 * @param index Extent index to set number to
153 * @param fblock Ohysical number of the block with child node
154 *
[728d771]155 */
156void ext4_extent_index_set_leaf(ext4_extent_index_t *index, uint64_t fblock)
[343ccfd]157{
[728d771]158 index->leaf_lo = host2uint32_t_le((fblock << 32) >> 32);
[38542dc]159 index->leaf_hi = host2uint16_t_le((uint16_t) (fblock >> 32));
[1a7756a]160}
161
[728d771]162/** Get magic value from extent header.
163 *
[38542dc]164 * @param header Extent header to load value from
165 *
166 * @return Magic value of extent header
167 *
[728d771]168 */
[acd869e]169uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
170{
171 return uint16_t_le2host(header->magic);
172}
173
[728d771]174/** Set magic value to extent header.
175 *
[38542dc]176 * @param header Extent header to set value to
177 * @param magic Magic value of extent header
178 *
[728d771]179 */
[343ccfd]180void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
181{
182 header->magic = host2uint16_t_le(magic);
183}
184
[728d771]185/** Get number of entries from extent header
186 *
[38542dc]187 * @param header Extent header to get value from
188 *
189 * @return Number of entries covered by extent header
190 *
[728d771]191 */
[acd869e]192uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
193{
194 return uint16_t_le2host(header->entries_count);
195}
196
[728d771]197/** Set number of entries to extent header
198 *
[38542dc]199 * @param header Extent header to set value to
200 * @param count Number of entries covered by extent header
201 *
[728d771]202 */
[343ccfd]203void ext4_extent_header_set_entries_count(ext4_extent_header_t *header,
[38542dc]204 uint16_t count)
[343ccfd]205{
206 header->entries_count = host2uint16_t_le(count);
207}
208
[728d771]209/** Get maximum number of entries from extent header
210 *
[38542dc]211 * @param header Extent header to get value from
212 *
213 * @return Maximum number of entries covered by extent header
214 *
[728d771]215 */
[acd869e]216uint16_t ext4_extent_header_get_max_entries_count(ext4_extent_header_t *header)
217{
218 return uint16_t_le2host(header->max_entries_count);
219}
220
[728d771]221/** Set maximum number of entries to extent header
222 *
[38542dc]223 * @param header Extent header to set value to
224 * @param max_count Maximum number of entries covered by extent header
225 *
[728d771]226 */
[343ccfd]227void ext4_extent_header_set_max_entries_count(ext4_extent_header_t *header,
[38542dc]228 uint16_t max_count)
[343ccfd]229{
230 header->max_entries_count = host2uint16_t_le(max_count);
231}
232
[728d771]233/** Get depth of extent subtree.
234 *
[38542dc]235 * @param header Extent header to get value from
236 *
237 * @return Depth of extent subtree
238 *
[728d771]239 */
[acd869e]240uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
241{
242 return uint16_t_le2host(header->depth);
243}
244
[728d771]245/** Set depth of extent subtree.
246 *
[38542dc]247 * @param header Extent header to set value to
248 * @param depth Depth of extent subtree
249 *
[728d771]250 */
[343ccfd]251void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
252{
253 header->depth = host2uint16_t_le(depth);
254}
255
[728d771]256/** Get generation from extent header
257 *
[38542dc]258 * @param header Extent header to get value from
259 *
260 * @return Generation
261 *
[728d771]262 */
[acd869e]263uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
264{
265 return uint32_t_le2host(header->generation);
266}
[829d238]267
[728d771]268/** Set generation to extent header
269 *
[38542dc]270 * @param header Extent header to set value to
271 * @param generation Generation
272 *
[728d771]273 */
[343ccfd]274void ext4_extent_header_set_generation(ext4_extent_header_t *header,
[38542dc]275 uint32_t generation)
[343ccfd]276{
277 header->generation = host2uint32_t_le(generation);
278}
279
[728d771]280/** Binary search in extent index node.
281 *
[38542dc]282 * @param header Extent header of index node
283 * @param index Output value - found index will be set here
284 * @param iblock Logical block number to find in index node
285 *
[fffb061]286 */
[47faec1]287static void ext4_extent_binsearch_idx(ext4_extent_header_t *header,
[38542dc]288 ext4_extent_index_t **index, uint32_t iblock)
[47faec1]289{
[38542dc]290 ext4_extent_index_t *r;
291 ext4_extent_index_t *l;
292 ext4_extent_index_t *m;
293
294 uint16_t entries_count =
295 ext4_extent_header_get_entries_count(header);
296
[06d85e5]297 /* Initialize bounds */
[47faec1]298 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
[79b5bc9]299 r = EXT4_EXTENT_FIRST_INDEX(header) + entries_count - 1;
[38542dc]300
[06d85e5]301 /* Do binary search */
[47faec1]302 while (l <= r) {
303 m = l + (r - l) / 2;
304 uint32_t first_block = ext4_extent_index_get_first_block(m);
[38542dc]305
306 if (iblock < first_block)
307 r = m - 1;
308 else
309 l = m + 1;
[47faec1]310 }
[38542dc]311
[06d85e5]312 /* Set output value */
[47faec1]313 *index = l - 1;
314}
315
[728d771]316/** Binary search in extent leaf node.
[38542dc]317 *
318 * @param header Extent header of leaf node
319 * @param extent Output value - found extent will be set here,
320 * or NULL if node is empty
321 * @param iblock Logical block number to find in leaf node
[728d771]322 *
[fffb061]323 */
[30ac3c3]324static void ext4_extent_binsearch(ext4_extent_header_t *header,
[38542dc]325 ext4_extent_t **extent, uint32_t iblock)
[a4419e7]326{
[38542dc]327 ext4_extent_t *r;
328 ext4_extent_t *l;
329 ext4_extent_t *m;
330
331 uint16_t entries_count =
332 ext4_extent_header_get_entries_count(header);
333
[a4419e7]334 if (entries_count == 0) {
[06d85e5]335 /* this leaf is empty */
[47faec1]336 *extent = NULL;
337 return;
338 }
[38542dc]339
[06d85e5]340 /* Initialize bounds */
[a4419e7]341 l = EXT4_EXTENT_FIRST(header) + 1;
[79b5bc9]342 r = EXT4_EXTENT_FIRST(header) + entries_count - 1;
[38542dc]343
[06d85e5]344 /* Do binary search */
[79b5bc9]345 while (l <= r) {
[a4419e7]346 m = l + (r - l) / 2;
[47faec1]347 uint32_t first_block = ext4_extent_get_first_block(m);
[38542dc]348
349 if (iblock < first_block)
350 r = m - 1;
351 else
352 l = m + 1;
[a4419e7]353 }
[38542dc]354
[06d85e5]355 /* Set output value */
[30ac3c3]356 *extent = l - 1;
[a4419e7]357}
358
[728d771]359/** Find physical block in the extent tree by logical block number.
360 *
361 * There is no need to save path in the tree during this algorithm.
362 *
[38542dc]363 * @param inode_ref I-node to load block from
364 * @param iblock Logical block number to find
365 * @param fblock Output value for physical block number
366 *
367 * @return Error code
368 *
[728d771]369 */
[38542dc]370int ext4_extent_find_block(ext4_inode_ref_t *inode_ref, uint32_t iblock,
371 uint32_t *fblock)
[e2629b08]372{
[532f53d]373 int rc;
[06d85e5]374 /* Compute bound defined by i-node size */
[38542dc]375 uint64_t inode_size =
376 ext4_inode_get_size(inode_ref->fs->superblock, inode_ref->inode);
377
378 uint32_t block_size =
379 ext4_superblock_get_block_size(inode_ref->fs->superblock);
380
[b73530a]381 uint32_t last_idx = (inode_size - 1) / block_size;
[38542dc]382
[06d85e5]383 /* Check if requested iblock is not over size of i-node */
[b73530a]384 if (iblock > last_idx) {
385 *fblock = 0;
386 return EOK;
387 }
[38542dc]388
389 block_t *block = NULL;
390
[06d85e5]391 /* Walk through extent tree */
[38542dc]392 ext4_extent_header_t *header =
393 ext4_inode_get_extent_header(inode_ref->inode);
394
[1ac1ab4]395 while (ext4_extent_header_get_depth(header) != 0) {
[06d85e5]396 /* Search index in node */
[1ac1ab4]397 ext4_extent_index_t *index;
398 ext4_extent_binsearch_idx(header, &index, iblock);
[38542dc]399
[06d85e5]400 /* Load child node and set values for the next iteration */
[1ac1ab4]401 uint64_t child = ext4_extent_index_get_leaf(index);
[38542dc]402
[532f53d]403 if (block != NULL) {
404 rc = block_put(block);
405 if (rc != EOK)
406 return rc;
407 }
[38542dc]408
[532f53d]409 rc = block_get(&block, inode_ref->fs->device, child,
[38542dc]410 BLOCK_FLAGS_NONE);
411 if (rc != EOK)
[47faec1]412 return rc;
[38542dc]413
[1ac1ab4]414 header = (ext4_extent_header_t *)block->data;
[47faec1]415 }
[38542dc]416
[06d85e5]417 /* Search extent in the leaf block */
[0d4db0f]418 ext4_extent_t* extent = NULL;
[1ac1ab4]419 ext4_extent_binsearch(header, &extent, iblock);
[38542dc]420
[06d85e5]421 /* Prevent empty leaf */
[1196df6]422 if (extent == NULL) {
423 *fblock = 0;
424 } else {
[06d85e5]425 /* Compute requested physical block address */
[1196df6]426 uint32_t phys_block;
[79b5bc9]427 uint32_t first = ext4_extent_get_first_block(extent);
428 phys_block = ext4_extent_get_start(extent) + iblock - first;
[38542dc]429
[1196df6]430 *fblock = phys_block;
431 }
[38542dc]432
[06d85e5]433 /* Cleanup */
[38542dc]434 if (block != NULL)
[532f53d]435 rc = block_put(block);
[38542dc]436
[532f53d]437 return rc;
[e2629b08]438}
439
[728d771]440/** Find extent for specified iblock.
441 *
442 * This function is used for finding block in the extent tree with
443 * saving the path through the tree for possible future modifications.
444 *
[38542dc]445 * @param inode_ref I-node to read extent tree from
446 * @param iblock Iblock to find extent for
447 * @param ret_path Output value for loaded path from extent tree
448 *
449 * @return Error code
450 *
[728d771]451 */
[38542dc]452static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref, uint32_t iblock,
453 ext4_extent_path_t **ret_path)
[0d4db0f]454{
455 ext4_extent_header_t *eh =
[38542dc]456 ext4_inode_get_extent_header(inode_ref->inode);
457
[0d4db0f]458 uint16_t depth = ext4_extent_header_get_depth(eh);
[38542dc]459
[0d4db0f]460 ext4_extent_path_t *tmp_path;
[38542dc]461
[06d85e5]462 /* Added 2 for possible tree growing */
[0d4db0f]463 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
[38542dc]464 if (tmp_path == NULL)
[0d4db0f]465 return ENOMEM;
[38542dc]466
[06d85e5]467 /* Initialize structure for algorithm start */
[0d4db0f]468 tmp_path[0].block = inode_ref->block;
469 tmp_path[0].header = eh;
[38542dc]470
[06d85e5]471 /* Walk through the extent tree */
[0d4db0f]472 uint16_t pos = 0;
[38542dc]473 int rc;
[0d4db0f]474 while (ext4_extent_header_get_depth(eh) != 0) {
[06d85e5]475 /* Search index in index node by iblock */
[38542dc]476 ext4_extent_binsearch_idx(tmp_path[pos].header,
477 &tmp_path[pos].index, iblock);
478
[0d4db0f]479 tmp_path[pos].depth = depth;
480 tmp_path[pos].extent = NULL;
[38542dc]481
[0d4db0f]482 assert(tmp_path[pos].index != NULL);
[38542dc]483
[06d85e5]484 /* Load information for the next iteration */
[0d4db0f]485 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
[38542dc]486
[0d4db0f]487 block_t *block;
[38542dc]488 rc = block_get(&block, inode_ref->fs->device, fblock,
489 BLOCK_FLAGS_NONE);
490 if (rc != EOK)
[6f413125]491 goto cleanup;
[38542dc]492
[0d4db0f]493 pos++;
[38542dc]494
[0d4db0f]495 eh = (ext4_extent_header_t *)block->data;
496 tmp_path[pos].block = block;
497 tmp_path[pos].header = eh;
498 }
[38542dc]499
[0d4db0f]500 tmp_path[pos].depth = 0;
501 tmp_path[pos].extent = NULL;
502 tmp_path[pos].index = NULL;
[38542dc]503
504 /* Find extent in the leaf node */
[0d4db0f]505 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
506 *ret_path = tmp_path;
[38542dc]507
[0d4db0f]508 return EOK;
[38542dc]509
[6f413125]510cleanup:
[532f53d]511 ;
512
513 int rc2 = EOK;
514
[38542dc]515 /*
516 * Put loaded blocks
[06d85e5]517 * From 1: 0 is a block with inode data
518 */
[6f413125]519 for (uint16_t i = 1; i < tmp_path->depth; ++i) {
[532f53d]520 if (tmp_path[i].block) {
521 rc2 = block_put(tmp_path[i].block);
522 if (rc == EOK && rc2 != EOK)
523 rc = rc2;
524 }
[6f413125]525 }
[38542dc]526
[06d85e5]527 /* Destroy temporary data structure */
[6f413125]528 free(tmp_path);
[38542dc]529
[6f413125]530 return rc;
[0d4db0f]531}
532
[728d771]533/** Release extent and all data blocks covered by the extent.
534 *
[38542dc]535 * @param inode_ref I-node to release extent and block from
536 * @param extent Extent to release
537 *
538 * @return Error code
539 *
[728d771]540 */
[38542dc]541static int ext4_extent_release(ext4_inode_ref_t *inode_ref,
542 ext4_extent_t *extent)
[0d4db0f]543{
[06d85e5]544 /* Compute number of the first physical block to release */
[5b0a3946]545 uint64_t start = ext4_extent_get_start(extent);
546 uint16_t block_count = ext4_extent_get_block_count(extent);
[38542dc]547
548 return ext4_balloc_free_blocks(inode_ref, start, block_count);
[5b0a3946]549}
550
[ee3b6150]551/** Recursively release the whole branch of the extent tree.
552 *
553 * For each entry of the node release the subbranch and finally release
554 * the node. In the leaf node all extents will be released.
555 *
[38542dc]556 * @param inode_ref I-node where the branch is released
557 * @param index Index in the non-leaf node to be released
558 * with the whole subtree
559 *
560 * @return Error code
561 *
[ee3b6150]562 */
[5b0a3946]563static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
564 ext4_extent_index_t *index)
565{
566 uint32_t fblock = ext4_extent_index_get_leaf(index);
[38542dc]567
568 block_t* block;
569 int rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
570 if (rc != EOK)
[5b0a3946]571 return rc;
[38542dc]572
[5b0a3946]573 ext4_extent_header_t *header = block->data;
[38542dc]574
[5b0a3946]575 if (ext4_extent_header_get_depth(header)) {
[06d85e5]576 /* The node is non-leaf, do recursion */
[5b0a3946]577 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
[38542dc]578
[06d85e5]579 /* Release all subbranches */
[38542dc]580 for (uint32_t i = 0;
581 i < ext4_extent_header_get_entries_count(header);
582 ++i, ++idx) {
[5b0a3946]583 rc = ext4_extent_release_branch(inode_ref, idx);
[38542dc]584 if (rc != EOK)
[5b0a3946]585 return rc;
586 }
587 } else {
[06d85e5]588 /* Leaf node reached */
[5b0a3946]589 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
[38542dc]590
[06d85e5]591 /* Release all extents and stop recursion */
[38542dc]592 for (uint32_t i = 0;
593 i < ext4_extent_header_get_entries_count(header);
594 ++i, ++ext) {
[5b0a3946]595 rc = ext4_extent_release(inode_ref, ext);
[38542dc]596 if (rc != EOK)
[5b0a3946]597 return rc;
598 }
599 }
[38542dc]600
[06d85e5]601 /* Release data block where the node was stored */
[38542dc]602
[5b0a3946]603 rc = block_put(block);
[38542dc]604 if (rc != EOK)
[5b0a3946]605 return rc;
[38542dc]606
[532f53d]607 return ext4_balloc_free_block(inode_ref, fblock);
[5b0a3946]608}
609
[ee3b6150]610/** Release all data blocks starting from specified logical block.
611 *
[38542dc]612 * @param inode_ref I-node to release blocks from
613 * @param iblock_from First logical block to release
614 *
[ee3b6150]615 */
[1196df6]616int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
[38542dc]617 uint32_t iblock_from)
[5b0a3946]618{
[06d85e5]619 /* Find the first extent to modify */
[0d4db0f]620 ext4_extent_path_t *path;
[38542dc]621 int rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
622 if (rc != EOK)
[0d4db0f]623 return rc;
[38542dc]624
[06d85e5]625 /* Jump to last item of the path (extent) */
[0d4db0f]626 ext4_extent_path_t *path_ptr = path;
[38542dc]627 while (path_ptr->depth != 0)
[0d4db0f]628 path_ptr++;
[38542dc]629
[0d4db0f]630 assert(path_ptr->extent != NULL);
[38542dc]631
[06d85e5]632 /* First extent maybe released partially */
[38542dc]633 uint32_t first_iblock =
634 ext4_extent_get_first_block(path_ptr->extent);
635 uint32_t first_fblock =
636 ext4_extent_get_start(path_ptr->extent) + iblock_from - first_iblock;
637
[0d4db0f]638 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
[38542dc]639
640 uint16_t delete_count = block_count -
641 (ext4_extent_get_start(path_ptr->extent) - first_fblock);
642
[06d85e5]643 /* Release all blocks */
[5b0a3946]644 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
[38542dc]645 if (rc != EOK)
[6f413125]646 goto cleanup;
[38542dc]647
[06d85e5]648 /* Correct counter */
[5b0a3946]649 block_count -= delete_count;
[0d4db0f]650 ext4_extent_set_block_count(path_ptr->extent, block_count);
[38542dc]651
[06d85e5]652 /* Initialize the following loop */
[38542dc]653 uint16_t entries =
654 ext4_extent_header_get_entries_count(path_ptr->header);
[3e2952b]655 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
656 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
[38542dc]657
[06d85e5]658 /* If first extent empty, release it */
[38542dc]659 if (block_count == 0)
[0d4db0f]660 entries--;
[38542dc]661
[06d85e5]662 /* Release all successors of the first extent in the same node */
[3e2952b]663 while (tmp_ext < stop_ext) {
664 first_fblock = ext4_extent_get_start(tmp_ext);
665 delete_count = ext4_extent_get_block_count(tmp_ext);
[38542dc]666
[3e2952b]667 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
[38542dc]668 if (rc != EOK)
[6f413125]669 goto cleanup;
[38542dc]670
[3e2952b]671 entries--;
672 tmp_ext++;
673 }
[38542dc]674
[49c94a3]675 ext4_extent_header_set_entries_count(path_ptr->header, entries);
676 path_ptr->block->dirty = true;
[38542dc]677
[06d85e5]678 /* If leaf node is empty, parent entry must be modified */
[d0c9b4b]679 bool remove_parent_record = false;
[38542dc]680
[06d85e5]681 /* Don't release root block (including inode data) !!! */
[3e2952b]682 if ((path_ptr != path) && (entries == 0)) {
683 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
[38542dc]684 if (rc != EOK)
[6f413125]685 goto cleanup;
[38542dc]686
[d0c9b4b]687 remove_parent_record = true;
[5b0a3946]688 }
[38542dc]689
[06d85e5]690 /* Jump to the parent */
[3e2952b]691 --path_ptr;
[38542dc]692
[06d85e5]693 /* Release all successors in all tree levels */
[ee3b6150]694 while (path_ptr >= path) {
[3e2952b]695 entries = ext4_extent_header_get_entries_count(path_ptr->header);
696 ext4_extent_index_t *index = path_ptr->index + 1;
697 ext4_extent_index_t *stop =
[38542dc]698 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
699
[06d85e5]700 /* Correct entries count because of changes in the previous iteration */
[38542dc]701 if (remove_parent_record)
[3e2952b]702 entries--;
[38542dc]703
[06d85e5]704 /* Iterate over all entries and release the whole subtrees */
[5b0a3946]705 while (index < stop) {
706 rc = ext4_extent_release_branch(inode_ref, index);
[38542dc]707 if (rc != EOK)
[6f413125]708 goto cleanup;
[38542dc]709
[5b0a3946]710 ++index;
[3e2952b]711 --entries;
[5b0a3946]712 }
[38542dc]713
[d0c9b4b]714 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[3e2952b]715 path_ptr->block->dirty = true;
[38542dc]716
[06d85e5]717 /* Free the node if it is empty */
[3e2952b]718 if ((entries == 0) && (path_ptr != path)) {
719 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
[38542dc]720 if (rc != EOK)
[6f413125]721 goto cleanup;
[38542dc]722
[06d85e5]723 /* Mark parent to be checked */
[d0c9b4b]724 remove_parent_record = true;
[38542dc]725 } else
[d0c9b4b]726 remove_parent_record = false;
[38542dc]727
[3e2952b]728 --path_ptr;
[0d4db0f]729 }
[38542dc]730
[6f413125]731cleanup:
[532f53d]732 ;
733
734 int rc2 = EOK;
735
[38542dc]736 /*
737 * Put loaded blocks
[06d85e5]738 * starting from 1: 0 is a block with inode data
739 */
[82cb6768]740 for (uint16_t i = 1; i <= path->depth; ++i) {
[532f53d]741 if (path[i].block) {
742 rc2 = block_put(path[i].block);
743 if (rc == EOK && rc2 != EOK)
744 rc = rc2;
745 }
[0d4db0f]746 }
[38542dc]747
[06d85e5]748 /* Destroy temporary data structure */
[0d4db0f]749 free(path);
[38542dc]750
[6f413125]751 return rc;
[0d4db0f]752}
[1ac1ab4]753
[9fc72fb3]754/** Append new extent to the i-node and do some splitting if necessary.
[ee3b6150]755 *
[38542dc]756 * @param inode_ref I-node to append extent to
757 * @param path Path in the extent tree for possible splitting
758 * @param last_path_item Input/output parameter for pointer to the last
759 * valid item in the extent tree path
760 * @param iblock Logical index of block to append extent for
761 *
762 * @return Error code
763 *
[ee3b6150]764 */
[82cb6768]765static int ext4_extent_append_extent(ext4_inode_ref_t *inode_ref,
[38542dc]766 ext4_extent_path_t *path, uint32_t iblock)
[82cb6768]767{
[5d3d75a5]768 ext4_extent_path_t *path_ptr = path + path->depth;
[38542dc]769
[82cb6768]770 uint32_t block_size =
[38542dc]771 ext4_superblock_get_block_size(inode_ref->fs->superblock);
772
[96606fe]773 /* Start splitting */
774 while (path_ptr > path) {
[38542dc]775 uint16_t entries =
776 ext4_extent_header_get_entries_count(path_ptr->header);
777 uint16_t limit =
778 ext4_extent_header_get_max_entries_count(path_ptr->header);
779
[96606fe]780 if (entries == limit) {
[5d3d75a5]781 /* Full node - allocate block for new one */
782 uint32_t fblock;
[38542dc]783 int rc = ext4_balloc_alloc_block(inode_ref, &fblock);
784 if (rc != EOK)
[96606fe]785 return rc;
[38542dc]786
[96606fe]787 block_t *block;
[38542dc]788 rc = block_get(&block, inode_ref->fs->device, fblock,
789 BLOCK_FLAGS_NOREAD);
[96606fe]790 if (rc != EOK) {
791 ext4_balloc_free_block(inode_ref, fblock);
792 return rc;
793 }
[38542dc]794
[5d3d75a5]795 /* Put back not modified old block */
[e5a1ace3]796 rc = block_put(path_ptr->block);
797 if (rc != EOK) {
798 ext4_balloc_free_block(inode_ref, fblock);
[bd5d4e1]799 block_put(block);
[e5a1ace3]800 return rc;
801 }
[38542dc]802
[5d3d75a5]803 /* Initialize newly allocated block and remember it */
804 memset(block->data, 0, block_size);
[96606fe]805 path_ptr->block = block;
[38542dc]806
[5d3d75a5]807 /* Update pointers in extent path structure */
808 path_ptr->header = block->data;
[96606fe]809 if (path_ptr->depth) {
810 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header);
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 limit = (block_size - sizeof(ext4_extent_header_t)) /
[38542dc]814 sizeof(ext4_extent_index_t);
[96606fe]815 } else {
816 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header);
817 ext4_extent_set_first_block(path_ptr->extent, iblock);
818 limit = (block_size - sizeof(ext4_extent_header_t)) /
[38542dc]819 sizeof(ext4_extent_t);
[96606fe]820 }
[38542dc]821
[6f10638]822 /* Initialize on-disk structure (header) */
[96606fe]823 ext4_extent_header_set_entries_count(path_ptr->header, 1);
824 ext4_extent_header_set_max_entries_count(path_ptr->header, limit);
[6f10638]825 ext4_extent_header_set_magic(path_ptr->header, EXT4_EXTENT_MAGIC);
826 ext4_extent_header_set_depth(path_ptr->header, path_ptr->depth);
827 ext4_extent_header_set_generation(path_ptr->header, 0);
[38542dc]828
[96606fe]829 path_ptr->block->dirty = true;
[38542dc]830
[5d3d75a5]831 /* Jump to the preceeding item */
[96606fe]832 path_ptr--;
833 } else {
834 /* Node with free space */
835 if (path_ptr->depth) {
836 path_ptr->index = EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
837 ext4_extent_index_set_first_block(path_ptr->index, iblock);
838 ext4_extent_index_set_leaf(path_ptr->index, (path_ptr + 1)->block->lba);
839 } else {
840 path_ptr->extent = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
841 ext4_extent_set_first_block(path_ptr->extent, iblock);
842 }
[38542dc]843
[96606fe]844 ext4_extent_header_set_entries_count(path_ptr->header, entries + 1);
845 path_ptr->block->dirty = true;
[38542dc]846
[5d3d75a5]847 /* No more splitting needed */
[96606fe]848 return EOK;
849 }
[82cb6768]850 }
[38542dc]851
[5d3d75a5]852 assert(path_ptr == path);
[38542dc]853
[96606fe]854 /* Should be the root split too? */
[38542dc]855
[8eff2b0]856 uint16_t entries = ext4_extent_header_get_entries_count(path->header);
857 uint16_t limit = ext4_extent_header_get_max_entries_count(path->header);
[38542dc]858
[8eff2b0]859 if (entries == limit) {
860 uint32_t new_fblock;
[38542dc]861 int rc = ext4_balloc_alloc_block(inode_ref, &new_fblock);
862 if (rc != EOK)
[8eff2b0]863 return rc;
[38542dc]864
[8eff2b0]865 block_t *block;
[38542dc]866 rc = block_get(&block, inode_ref->fs->device, new_fblock,
867 BLOCK_FLAGS_NOREAD);
868 if (rc != EOK)
[8eff2b0]869 return rc;
[38542dc]870
[8eff2b0]871 /* Initialize newly allocated block */
872 memset(block->data, 0, block_size);
[38542dc]873
[8eff2b0]874 /* Move data from root to the new block */
875 memcpy(block->data, inode_ref->inode->blocks,
[38542dc]876 EXT4_INODE_BLOCKS * sizeof(uint32_t));
877
878 /* Data block is initialized */
879
[8eff2b0]880 block_t *root_block = path->block;
881 uint16_t root_depth = path->depth;
882 ext4_extent_header_t *root_header = path->header;
[38542dc]883
[8eff2b0]884 /* Make space for tree growing */
885 ext4_extent_path_t *new_root = path;
886 ext4_extent_path_t *old_root = path + 1;
[38542dc]887
[8eff2b0]888 size_t nbytes = sizeof(ext4_extent_path_t) * (path->depth + 1);
889 memmove(old_root, new_root, nbytes);
890 memset(new_root, 0, sizeof(ext4_extent_path_t));
[38542dc]891
[8eff2b0]892 /* Update old root structure */
893 old_root->block = block;
894 old_root->header = (ext4_extent_header_t *)block->data;
[38542dc]895
[8eff2b0]896 /* Add new entry and update limit for entries */
897 if (old_root->depth) {
898 limit = (block_size - sizeof(ext4_extent_header_t)) /
[38542dc]899 sizeof(ext4_extent_index_t);
[8eff2b0]900 old_root->index = EXT4_EXTENT_FIRST_INDEX(old_root->header) + entries;
901 ext4_extent_index_set_first_block(old_root->index, iblock);
902 ext4_extent_index_set_leaf(old_root->index, (old_root + 1)->block->lba);
903 old_root->extent = NULL;
904 } else {
905 limit = (block_size - sizeof(ext4_extent_header_t)) /
[38542dc]906 sizeof(ext4_extent_t);
[8eff2b0]907 old_root->extent = EXT4_EXTENT_FIRST(old_root->header) + entries;
908 ext4_extent_set_first_block(old_root->extent, iblock);
909 old_root->index = NULL;
910 }
[38542dc]911
[8eff2b0]912 ext4_extent_header_set_entries_count(old_root->header, entries + 1);
913 ext4_extent_header_set_max_entries_count(old_root->header, limit);
[38542dc]914
[8eff2b0]915 old_root->block->dirty = true;
[38542dc]916
[8eff2b0]917 /* Re-initialize new root metadata */
918 new_root->depth = root_depth + 1;
919 new_root->block = root_block;
920 new_root->header = root_header;
921 new_root->extent = NULL;
922 new_root->index = EXT4_EXTENT_FIRST_INDEX(new_root->header);
[38542dc]923
[8eff2b0]924 ext4_extent_header_set_depth(new_root->header, new_root->depth);
[38542dc]925
[8eff2b0]926 /* Create new entry in root */
927 ext4_extent_header_set_entries_count(new_root->header, 1);
928 ext4_extent_index_set_first_block(new_root->index, 0);
929 ext4_extent_index_set_leaf(new_root->index, new_fblock);
[38542dc]930
[8eff2b0]931 new_root->block->dirty = true;
932 } else {
933 if (path->depth) {
934 path->index = EXT4_EXTENT_FIRST_INDEX(path->header) + entries;
935 ext4_extent_index_set_first_block(path->index, iblock);
936 ext4_extent_index_set_leaf(path->index, (path + 1)->block->lba);
[6f10638]937 } else {
[8eff2b0]938 path->extent = EXT4_EXTENT_FIRST(path->header) + entries;
939 ext4_extent_set_first_block(path->extent, iblock);
[96606fe]940 }
[38542dc]941
[8eff2b0]942 ext4_extent_header_set_entries_count(path->header, entries + 1);
943 path->block->dirty = true;
[82cb6768]944 }
[38542dc]945
[82cb6768]946 return EOK;
947}
948
[81a7858]949/** Append data block to the i-node.
[9fc72fb3]950 *
[81a7858]951 * This function allocates data block, tries to append it
952 * to some existing extent or creates new extents.
953 * It includes possible extent tree modifications (splitting).
[5d3d75a5]954 *<
[38542dc]955 * @param inode_ref I-node to append block to
956 * @param iblock Output logical number of newly allocated block
957 * @param fblock Output physical block address of newly allocated block
958 *
959 * @return Error code
960 *
[9fc72fb3]961 */
[38542dc]962int ext4_extent_append_block(ext4_inode_ref_t *inode_ref, uint32_t *iblock,
963 uint32_t *fblock, bool update_size)
[ce6de59]964{
965 ext4_superblock_t *sb = inode_ref->fs->superblock;
966 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
967 uint32_t block_size = ext4_superblock_get_block_size(sb);
[38542dc]968
[06d85e5]969 /* Calculate number of new logical block */
[b73530a]970 uint32_t new_block_idx = 0;
971 if (inode_size > 0) {
[38542dc]972 if ((inode_size % block_size) != 0)
[b73530a]973 inode_size += block_size - (inode_size % block_size);
[38542dc]974
[b73530a]975 new_block_idx = inode_size / block_size;
976 }
[38542dc]977
[06d85e5]978 /* Load the nearest leaf (with extent) */
[ce6de59]979 ext4_extent_path_t *path;
[38542dc]980 int rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
981 if (rc != EOK)
[ce6de59]982 return rc;
[38542dc]983
[06d85e5]984 /* Jump to last item of the path (extent) */
[ce6de59]985 ext4_extent_path_t *path_ptr = path;
[38542dc]986 while (path_ptr->depth != 0)
[ce6de59]987 path_ptr++;
[38542dc]988
[06d85e5]989 /* Add new extent to the node if not present */
[38542dc]990 if (path_ptr->extent == NULL)
[82cb6768]991 goto append_extent;
[38542dc]992
[82cb6768]993 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
994 uint16_t block_limit = (1 << 15);
[38542dc]995
[b6d7b7c]996 uint32_t phys_block = 0;
[82cb6768]997 if (block_count < block_limit) {
[06d85e5]998 /* There is space for new block in the extent */
[82cb6768]999 if (block_count == 0) {
[06d85e5]1000 /* Existing extent is empty */
[82cb6768]1001 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
[38542dc]1002 if (rc != EOK)
[7eb033ce]1003 goto finish;
[38542dc]1004
[06d85e5]1005 /* Initialize extent */
[82cb6768]1006 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1007 ext4_extent_set_start(path_ptr->extent, phys_block);
1008 ext4_extent_set_block_count(path_ptr->extent, 1);
[38542dc]1009
[06d85e5]1010 /* Update i-node */
[d510ac01]1011 if (update_size) {
1012 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1013 inode_ref->dirty = true;
1014 }
[38542dc]1015
[82cb6768]1016 path_ptr->block->dirty = true;
[38542dc]1017
[6f413125]1018 goto finish;
[82cb6768]1019 } else {
[06d85e5]1020 /* Existing extent contains some blocks */
[82cb6768]1021 phys_block = ext4_extent_get_start(path_ptr->extent);
1022 phys_block += ext4_extent_get_block_count(path_ptr->extent);
[38542dc]1023
[06d85e5]1024 /* Check if the following block is free for allocation */
[82cb6768]1025 bool free;
1026 rc = ext4_balloc_try_alloc_block(inode_ref, phys_block, &free);
[38542dc]1027 if (rc != EOK)
[7eb033ce]1028 goto finish;
[38542dc]1029
1030 if (!free) {
1031 /* Target is not free, new block must be appended to new extent */
[82cb6768]1032 goto append_extent;
1033 }
[38542dc]1034
[06d85e5]1035 /* Update extent */
[82cb6768]1036 ext4_extent_set_block_count(path_ptr->extent, block_count + 1);
[38542dc]1037
[06d85e5]1038 /* Update i-node */
[d510ac01]1039 if (update_size) {
1040 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1041 inode_ref->dirty = true;
1042 }
[38542dc]1043
[82cb6768]1044 path_ptr->block->dirty = true;
[38542dc]1045
[82cb6768]1046 goto finish;
1047 }
1048 }
[38542dc]1049
1050
[82cb6768]1051append_extent:
[38542dc]1052 /* Append new extent to the tree */
[82cb6768]1053 phys_block = 0;
[38542dc]1054
[06d85e5]1055 /* Allocate new data block */
[82cb6768]1056 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
[38542dc]1057 if (rc != EOK)
[82cb6768]1058 goto finish;
[38542dc]1059
[d510ac01]1060 /* Append extent for new block (includes tree splitting if needed) */
[5d3d75a5]1061 rc = ext4_extent_append_extent(inode_ref, path, new_block_idx);
[d510ac01]1062 if (rc != EOK) {
1063 ext4_balloc_free_block(inode_ref, phys_block);
1064 goto finish;
1065 }
[38542dc]1066
[5d3d75a5]1067 uint32_t tree_depth = ext4_extent_header_get_depth(path->header);
1068 path_ptr = path + tree_depth;
[38542dc]1069
[06d85e5]1070 /* Initialize newly created extent */
[82cb6768]1071 ext4_extent_set_block_count(path_ptr->extent, 1);
1072 ext4_extent_set_first_block(path_ptr->extent, new_block_idx);
1073 ext4_extent_set_start(path_ptr->extent, phys_block);
[38542dc]1074
[06d85e5]1075 /* Update i-node */
[d510ac01]1076 if (update_size) {
1077 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1078 inode_ref->dirty = true;
1079 }
[38542dc]1080
[82cb6768]1081 path_ptr->block->dirty = true;
[38542dc]1082
[81a7858]1083finish:
[532f53d]1084 ;
1085
1086 int rc2 = EOK;
1087
[06d85e5]1088 /* Set return values */
[1196df6]1089 *iblock = new_block_idx;
1090 *fblock = phys_block;
[38542dc]1091
1092 /*
1093 * Put loaded blocks
[06d85e5]1094 * starting from 1: 0 is a block with inode data
1095 */
[82cb6768]1096 for (uint16_t i = 1; i <= path->depth; ++i) {
[532f53d]1097 if (path[i].block) {
1098 rc2 = block_put(path[i].block);
1099 if (rc == EOK && rc2 != EOK)
1100 rc = rc2;
1101 }
[ce6de59]1102 }
[38542dc]1103
[06d85e5]1104 /* Destroy temporary data structure */
[ce6de59]1105 free(path);
[38542dc]1106
[6f413125]1107 return rc;
[ce6de59]1108}
1109
[829d238]1110/**
1111 * @}
[38542dc]1112 */
Note: See TracBrowser for help on using the repository browser.