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

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

First part → allocating first block to extent (not for extent with some blocks)

  • Property mode set to 100644
File size: 14.7 KB
RevLine 
[829d238]1/*
2 * Copyright (c) 2011 Frantisek Princ
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libext4
30 * @{
31 */
32
33/**
34 * @file libext4_extent.c
[c25e39b]35 * @brief Ext4 extent structures operations.
[829d238]36 */
37
[acd869e]38#include <byteorder.h>
[e2629b08]39#include <errno.h>
40#include <malloc.h>
41#include "libext4.h"
[829d238]42
[8958a26]43uint32_t ext4_extent_get_first_block(ext4_extent_t *extent)
44{
45 return uint32_t_le2host(extent->first_block);
46}
47
[343ccfd]48void ext4_extent_set_first_block(ext4_extent_t *extent, uint32_t first_block)
49{
50 extent->first_block = host2uint32_t_le(first_block);
51}
52
[8958a26]53uint16_t ext4_extent_get_block_count(ext4_extent_t *extent)
54{
55 return uint16_t_le2host(extent->block_count);
56}
57
[343ccfd]58void ext4_extent_set_block_count(ext4_extent_t *extent, uint16_t block_count)
59{
60 extent->block_count = host2uint16_t_le(block_count);
61}
62
[8958a26]63uint64_t ext4_extent_get_start(ext4_extent_t *extent)
64{
65 return ((uint64_t)uint16_t_le2host(extent->start_hi)) << 32 |
66 ((uint64_t)uint32_t_le2host(extent->start_lo));
[343ccfd]67}
[8958a26]68
[343ccfd]69void ext4_extent_set_start(ext4_extent_t *extent, uint64_t start)
70{
71 extent->start_lo = host2uint32_t_le((start << 32) >> 32);
72 extent->start_hi = host2uint16_t_le((uint16_t)(start >> 32));
[8958a26]73}
74
[1a7756a]75uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
76{
77 return uint32_t_le2host(index->first_block);
78}
79
[343ccfd]80void ext4_extent_index_set_first_block(ext4_extent_index_t *index,
81 uint32_t first)
82{
83 index->first_block = host2uint32_t_le(first);
84}
85
[1a7756a]86uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
87{
88 return ((uint64_t)uint16_t_le2host(index->leaf_hi)) << 32 |
[343ccfd]89 ((uint64_t)uint32_t_le2host(index->leaf_lo));
90}
91
92void ext4_extent_index_set_leaf(ext4_extent_index_t *index, uint64_t leaf)
93{
94 index->leaf_lo = host2uint32_t_le((leaf << 32) >> 32);
95 index->leaf_hi = host2uint16_t_le((uint16_t)(leaf >> 32));
[1a7756a]96}
97
[acd869e]98uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
99{
100 return uint16_t_le2host(header->magic);
101}
102
[343ccfd]103void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
104{
105 header->magic = host2uint16_t_le(magic);
106}
107
[acd869e]108uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
109{
110 return uint16_t_le2host(header->entries_count);
111}
112
[343ccfd]113void ext4_extent_header_set_entries_count(ext4_extent_header_t *header,
114 uint16_t count)
115{
116 header->entries_count = host2uint16_t_le(count);
117}
118
[acd869e]119uint16_t ext4_extent_header_get_max_entries_count(ext4_extent_header_t *header)
120{
121 return uint16_t_le2host(header->max_entries_count);
122}
123
[343ccfd]124void ext4_extent_header_set_max_entries_count(ext4_extent_header_t *header,
125 uint16_t max_count)
126{
127 header->max_entries_count = host2uint16_t_le(max_count);
128}
129
[acd869e]130uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
131{
132 return uint16_t_le2host(header->depth);
133}
134
[343ccfd]135void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
136{
137 header->depth = host2uint16_t_le(depth);
138}
139
[acd869e]140uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
141{
142 return uint32_t_le2host(header->generation);
143}
[829d238]144
[343ccfd]145void ext4_extent_header_set_generation(ext4_extent_header_t *header,
146 uint32_t generation)
147{
148 header->generation = host2uint32_t_le(generation);
149}
150
[fffb061]151/**
152 * Binary search in extent index node
153 */
[47faec1]154static void ext4_extent_binsearch_idx(ext4_extent_header_t *header,
155 ext4_extent_index_t **index, uint32_t iblock)
156{
157 ext4_extent_index_t *r, *l, *m;
158
159 uint16_t entries_count = ext4_extent_header_get_entries_count(header);
160
161 if (entries_count == 1) {
162 *index = EXT4_EXTENT_FIRST_INDEX(header);
163 return;
164 }
165
166 l = EXT4_EXTENT_FIRST_INDEX(header) + 1;
167 r = l + entries_count - 1;
168
169 while (l <= r) {
170 m = l + (r - l) / 2;
171 uint32_t first_block = ext4_extent_index_get_first_block(m);
172 if (iblock < first_block) {
173 r = m - 1;
174 } else {
175 l = m + 1;
176 }
177 }
178
179 *index = l - 1;
180}
181
[fffb061]182/**
183 * Binary search in extent leaf node
184 */
[30ac3c3]185static void ext4_extent_binsearch(ext4_extent_header_t *header,
186 ext4_extent_t **extent, uint32_t iblock)
[a4419e7]187{
188 ext4_extent_t *r, *l, *m;
189
190 uint16_t entries_count = ext4_extent_header_get_entries_count(header);
191
192 if (entries_count == 0) {
193 // this leaf is empty
[47faec1]194 EXT4FS_DBG("EMPTY LEAF");
195 *extent = NULL;
196 return;
197 }
198
199 if (entries_count == 1) {
200 *extent = EXT4_EXTENT_FIRST(header);
[a4419e7]201 return;
202 }
203
204 l = EXT4_EXTENT_FIRST(header) + 1;
205 r = l + entries_count - 1;
[9104bb5]206
[0d4db0f]207 while (l < r) {
[a4419e7]208 m = l + (r - l) / 2;
[47faec1]209 uint32_t first_block = ext4_extent_get_first_block(m);
210 if (iblock < first_block) {
[a4419e7]211 r = m - 1;
212 } else {
213 l = m + 1;
214 }
215 }
216
[30ac3c3]217 *extent = l - 1;
[a4419e7]218}
219
[1ac1ab4]220// Reading routine without saving blocks to path - for saving memory during finding block
221int ext4_extent_find_block(ext4_inode_ref_t *inode_ref, uint32_t iblock, uint32_t *fblock)
[e2629b08]222{
[9104bb5]223 int rc;
[e2629b08]224
[1ac1ab4]225 block_t* block = NULL;
[e2629b08]226
[1ac1ab4]227 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode_ref->inode);
228 while (ext4_extent_header_get_depth(header) != 0) {
[e2629b08]229
[1ac1ab4]230 ext4_extent_index_t *index;
231 ext4_extent_binsearch_idx(header, &index, iblock);
[fffb061]232
[1ac1ab4]233 uint64_t child = ext4_extent_index_get_leaf(index);
[fffb061]234
[1ac1ab4]235 if (block != NULL) {
236 block_put(block);
237 }
[e2629b08]238
[1ac1ab4]239 rc = block_get(&block, inode_ref->fs->device, child, BLOCK_FLAGS_NONE);
[47faec1]240 if (rc != EOK) {
241 return rc;
242 }
[e2629b08]243
[1ac1ab4]244 header = (ext4_extent_header_t *)block->data;
[47faec1]245 }
[e2629b08]246
247
[0d4db0f]248 ext4_extent_t* extent = NULL;
[1ac1ab4]249 ext4_extent_binsearch(header, &extent, iblock);
[e2629b08]250
[0d4db0f]251 assert(extent != NULL);
[fffb061]252
[9104bb5]253 uint32_t phys_block;
254 phys_block = ext4_extent_get_start(extent) + iblock;
255 phys_block -= ext4_extent_get_first_block(extent);
[e2629b08]256
[9104bb5]257 *fblock = phys_block;
[e2629b08]258
[1ac1ab4]259 if (block != NULL) {
260 block_put(block);
[fffb061]261 }
[9104bb5]262
[fffb061]263
264 return EOK;
[e2629b08]265}
266
[0d4db0f]267static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref,
268 uint32_t iblock, ext4_extent_path_t **ret_path)
269{
270 int rc;
271
272 ext4_extent_header_t *eh =
273 ext4_inode_get_extent_header(inode_ref->inode);
274
275 uint16_t depth = ext4_extent_header_get_depth(eh);
276
277 ext4_extent_path_t *tmp_path;
278
279 // Added 2 for possible tree growing
280 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
281 if (tmp_path == NULL) {
282 return ENOMEM;
283 }
284
285 tmp_path[0].block = inode_ref->block;
286 tmp_path[0].header = eh;
287
288 uint16_t pos = 0;
289 while (ext4_extent_header_get_depth(eh) != 0) {
290
291 ext4_extent_binsearch_idx(tmp_path[pos].header, &tmp_path[pos].index, iblock);
292
293 tmp_path[pos].depth = depth;
294 tmp_path[pos].extent = NULL;
295
296 assert(tmp_path[pos].index != NULL);
297
298 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
299
300 block_t *block;
301 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
302 if (rc != EOK) {
303 // TODO cleanup
304 EXT4FS_DBG("ERRRR");
305 return rc;
306 }
307
308 pos++;
309
310 eh = (ext4_extent_header_t *)block->data;
311 tmp_path[pos].block = block;
312 tmp_path[pos].header = eh;
313
314 }
315
316 tmp_path[pos].depth = 0;
317 tmp_path[pos].extent = NULL;
318 tmp_path[pos].index = NULL;
319
320 /* find extent */
321 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
322
323 *ret_path = tmp_path;
324
325 return EOK;
326}
327
[5b0a3946]328static int ext4_extent_release(ext4_inode_ref_t *inode_ref, ext4_extent_t* extent)
[0d4db0f]329{
330 int rc;
331
[5b0a3946]332 uint64_t start = ext4_extent_get_start(extent);
333 uint16_t block_count = ext4_extent_get_block_count(extent);
334
335 rc = ext4_balloc_free_blocks(inode_ref, start, block_count);
336 if (rc != EOK) {
337 EXT4FS_DBG("ERROR");
338 return rc;
339 }
340
341 return EOK;
342}
343
344// Recursive release
345static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
346 ext4_extent_index_t *index)
347{
348 int rc;
349
350 block_t* block;
351
352 uint32_t fblock = ext4_extent_index_get_leaf(index);
353
354 EXT4FS_DBG("fblock = \%u", fblock);
355
356 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
357 if (rc != EOK) {
358 EXT4FS_DBG("ERROR get_block");
359 return rc;
360 }
361
362 ext4_extent_header_t *header = block->data;
363
364 if (ext4_extent_header_get_depth(header)) {
365
366 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
367
368 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++idx) {
369 rc = ext4_extent_release_branch(inode_ref, idx);
370 if (rc != EOK) {
371 EXT4FS_DBG("error recursion");
372 return rc;
373 }
374 }
375 } else {
376 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
377
378 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++ext) {
379 rc = ext4_extent_release(inode_ref, ext);
380 if (rc != EOK) {
381 EXT4FS_DBG("error recursion");
382 return rc;
383 }
384 }
385 }
386
387 rc = block_put(block);
388 if (rc != EOK) {
389 EXT4FS_DBG("ERROR put_block");
390 return rc;
391 }
392
393 ext4_balloc_free_block(inode_ref, fblock);
394
395 return EOK;
396}
397
398int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref, uint32_t iblock_from)
399{
400 int rc;
401
[3e2952b]402 // Find the first extent to modify
[0d4db0f]403 ext4_extent_path_t *path;
[5b0a3946]404 rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
[0d4db0f]405 if (rc != EOK) {
406 return rc;
407 }
408
[3e2952b]409 // Jump to last item of the path (extent)
[0d4db0f]410 ext4_extent_path_t *path_ptr = path;
411 while (path_ptr->depth != 0) {
412 path_ptr++;
413 }
414
415 assert(path_ptr->extent != NULL);
416
[3e2952b]417 // First extent maybe released partially
[5b0a3946]418 uint32_t first_fblock;
419 first_fblock = ext4_extent_get_start(path_ptr->extent) + iblock_from;
420 first_fblock -= ext4_extent_get_first_block(path_ptr->extent);
[0d4db0f]421
422 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
423
[5b0a3946]424 uint16_t delete_count = block_count - first_fblock +
425 ext4_extent_get_start(path_ptr->extent);
426
427 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
[3e2952b]428 if (rc != EOK) {
429 // TODO goto cleanup
430 EXT4FS_DBG("ERROR");
431 return rc;
432 }
[0d4db0f]433
[5b0a3946]434 block_count -= delete_count;
[0d4db0f]435 ext4_extent_set_block_count(path_ptr->extent, block_count);
436
[3e2952b]437 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
438 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
439 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
[5b0a3946]440
[3e2952b]441 // If first extent empty, release it
[0d4db0f]442 if (block_count == 0) {
443 entries--;
444 ext4_extent_header_set_entries_count(path_ptr->header, entries);
445 }
446
[3e2952b]447 // Release all successors of the first extent in the same node
448 while (tmp_ext < stop_ext) {
449 first_fblock = ext4_extent_get_start(tmp_ext);
450 delete_count = ext4_extent_get_block_count(tmp_ext);
[5b0a3946]451
[3e2952b]452 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
453 if (rc != EOK) {
454 // TODO goto cleanup
455 EXT4FS_DBG("ERROR");
456 return rc;
457 }
[5b0a3946]458
[3e2952b]459 entries--;
460 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[5b0a3946]461
[3e2952b]462 tmp_ext++;
463 }
[5b0a3946]464
[3e2952b]465 // If leaf node is empty, the whole tree must be checked and the node will be released
466 bool check_tree = false;
[5b0a3946]467
[3e2952b]468 // Don't release root block (including inode data) !!!
469 if ((path_ptr != path) && (entries == 0)) {
470 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
471 if (rc != EOK) {
472 EXT4FS_DBG("ERROR");
473 // TODO goto cleanup
474 return rc;
[001307cf]475 }
[3e2952b]476 check_tree = true;
[5b0a3946]477 }
[001307cf]478
[3e2952b]479 // Jump to the parent
480 --path_ptr;
[5b0a3946]481
[3e2952b]482 // release all successors in all levels
483 while(path_ptr >= path) {
484 entries = ext4_extent_header_get_entries_count(path_ptr->header);
485 ext4_extent_index_t *index = path_ptr->index + 1;
486 ext4_extent_index_t *stop =
487 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
[001307cf]488
[3e2952b]489 if (check_tree) {
490 entries--;
491 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[001307cf]492 }
[0d4db0f]493
[5b0a3946]494 while (index < stop) {
495 rc = ext4_extent_release_branch(inode_ref, index);
496 if (rc != EOK) {
497 EXT4FS_DBG("ERR");
[3e2952b]498 // TODO goto cleanup
499 return rc;
[5b0a3946]500 }
501 ++index;
[3e2952b]502 --entries;
503 ext4_extent_header_set_entries_count(path_ptr->header, entries);
[5b0a3946]504 }
505
[3e2952b]506 path_ptr->block->dirty = true;
[5b0a3946]507
[3e2952b]508 if ((entries == 0) && (path_ptr != path)) {
509 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
[5b0a3946]510 if (rc != EOK) {
[3e2952b]511 EXT4FS_DBG("ERROR");
512 // TODO goto cleanup
513 return rc;
[5b0a3946]514 }
[3e2952b]515 check_tree = true;
516 } else {
517 check_tree = false;
[5b0a3946]518 }
[3e2952b]519
520 --path_ptr;
[0d4db0f]521 }
522
[3e2952b]523
524 // Finish
[0d4db0f]525 uint16_t depth = path->depth;
526
527 // Put loaded blocks
528 // From 1 -> 0 is a block with inode data
529 for (uint16_t i = 1; i < depth; ++i) {
530 if (path[i].block) {
531 block_put(path[i].block);
532 }
533 }
534
535 // Destroy temporary data structure
536 free(path);
537
538 return EOK;
539}
[1ac1ab4]540
[ce6de59]541int ext4_extent_append_block(ext4_inode_ref_t *inode_ref,
542 uint32_t *iblock, uint32_t *fblock)
543{
544 int rc;
545
546 ext4_superblock_t *sb = inode_ref->fs->superblock;
547 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
548
549 ext4_extent_header_t *header =
550 ext4_inode_get_extent_header(inode_ref->inode);
551
552 // Initialize if empty inode
553 if (inode_size == 0) {
554 ext4_extent_t *first = EXT4_EXTENT_FIRST(header);
555 ext4_extent_set_block_count(first, 0);
556 ext4_extent_set_first_block(first, 0);
557 ext4_extent_set_start(first, 0);
558
559 ext4_extent_header_set_depth(header, 0);
560 ext4_extent_header_set_entries_count(header, 1);
561 }
562
563 uint32_t block_size = ext4_superblock_get_block_size(sb);
564 uint32_t new_block_idx = inode_size / block_size;
565
566 ext4_extent_path_t *path;
567 rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
568 if (rc != EOK) {
569 EXT4FS_DBG("find extent ERROR");
570 return rc;
571 }
572
573 // Jump to last item of the path (extent)
574 ext4_extent_path_t *path_ptr = path;
575 while (path_ptr->depth != 0) {
576 path_ptr++;
577 }
578
579 // Check if extent exists
580 assert(path_ptr->extent != NULL);
581
582 uint32_t phys_block;
583
584 if (ext4_extent_get_block_count(path_ptr->extent) == 0) {
585
586 // Add first block to the extent
587
588 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
589 if (rc != EOK) {
590 EXT4FS_DBG("ERRO in balloc");
591 return rc;
592 }
593
594 ext4_extent_set_block_count(path_ptr->extent, 1);
595 ext4_extent_set_start(path_ptr->extent, phys_block);
596
597 path_ptr->block->dirty = true;
598
599 goto finish;
600
601 } else {
602 // try allocate succeeding extent block
603
604 // TODO
605 assert(false);
606
607 }
608
609
610finish:
611 // Put loaded blocks
612 // From 1 -> 0 is a block with inode data
613 for (uint16_t i = 1; i < path->depth; ++i) {
614 if (path[i].block) {
615 block_put(path[i].block);
616 }
617 }
618
619 // Destroy temporary data structure
620 free(path);
621
622 return EOK;
623}
624
[829d238]625/**
626 * @}
627 */
Note: See TracBrowser for help on using the repository browser.