source: mainline/uspace/lib/ext4/src/extent.c@ 84239b1

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

And there was much fixing.

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