source: mainline/uspace/lib/ext4/src/extent.c@ 38d150e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 38d150e was 38d150e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Prefer to get memory allocation functions through the standard stdlib header.

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