source: mainline/uspace/lib/ext4/libext4_extent.c@ 936132f

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

extent file initialization and allocation

  • Property mode set to 100644
File size: 14.5 KB
Line 
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
35 * @brief Ext4 extent structures operations.
36 */
37
38#include <byteorder.h>
39#include <errno.h>
40#include <malloc.h>
41#include "libext4.h"
42
43uint32_t ext4_extent_get_first_block(ext4_extent_t *extent)
44{
45 return uint32_t_le2host(extent->first_block);
46}
47
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
53uint16_t ext4_extent_get_block_count(ext4_extent_t *extent)
54{
55 return uint16_t_le2host(extent->block_count);
56}
57
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
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));
67}
68
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));
73}
74
75uint32_t ext4_extent_index_get_first_block(ext4_extent_index_t *index)
76{
77 return uint32_t_le2host(index->first_block);
78}
79
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
86uint64_t ext4_extent_index_get_leaf(ext4_extent_index_t *index)
87{
88 return ((uint64_t)uint16_t_le2host(index->leaf_hi)) << 32 |
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));
96}
97
98uint16_t ext4_extent_header_get_magic(ext4_extent_header_t *header)
99{
100 return uint16_t_le2host(header->magic);
101}
102
103void ext4_extent_header_set_magic(ext4_extent_header_t *header, uint16_t magic)
104{
105 header->magic = host2uint16_t_le(magic);
106}
107
108uint16_t ext4_extent_header_get_entries_count(ext4_extent_header_t *header)
109{
110 return uint16_t_le2host(header->entries_count);
111}
112
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
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
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
130uint16_t ext4_extent_header_get_depth(ext4_extent_header_t *header)
131{
132 return uint16_t_le2host(header->depth);
133}
134
135void ext4_extent_header_set_depth(ext4_extent_header_t *header, uint16_t depth)
136{
137 header->depth = host2uint16_t_le(depth);
138}
139
140uint32_t ext4_extent_header_get_generation(ext4_extent_header_t *header)
141{
142 return uint32_t_le2host(header->generation);
143}
144
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
151/**
152 * Binary search in extent index node
153 */
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
182/**
183 * Binary search in extent leaf node
184 */
185static void ext4_extent_binsearch(ext4_extent_header_t *header,
186 ext4_extent_t **extent, uint32_t iblock)
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
194 EXT4FS_DBG("EMPTY LEAF");
195 *extent = NULL;
196 return;
197 }
198
199 if (entries_count == 1) {
200 *extent = EXT4_EXTENT_FIRST(header);
201 return;
202 }
203
204 l = EXT4_EXTENT_FIRST(header) + 1;
205 r = l + entries_count - 1;
206
207 while (l < r) {
208 m = l + (r - l) / 2;
209 uint32_t first_block = ext4_extent_get_first_block(m);
210 if (iblock < first_block) {
211 r = m - 1;
212 } else {
213 l = m + 1;
214 }
215 }
216
217 *extent = l - 1;
218}
219
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)
222{
223 int rc;
224
225 block_t* block = NULL;
226
227 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode_ref->inode);
228 while (ext4_extent_header_get_depth(header) != 0) {
229
230 ext4_extent_index_t *index;
231 ext4_extent_binsearch_idx(header, &index, iblock);
232
233 uint64_t child = ext4_extent_index_get_leaf(index);
234
235 if (block != NULL) {
236 block_put(block);
237 }
238
239 rc = block_get(&block, inode_ref->fs->device, child, BLOCK_FLAGS_NONE);
240 if (rc != EOK) {
241 return rc;
242 }
243
244 header = (ext4_extent_header_t *)block->data;
245 }
246
247
248 ext4_extent_t* extent = NULL;
249 ext4_extent_binsearch(header, &extent, iblock);
250
251 if (extent == NULL) {
252 *fblock = 0;
253 } else {
254 uint32_t phys_block;
255 phys_block = ext4_extent_get_start(extent) + iblock;
256 phys_block -= ext4_extent_get_first_block(extent);
257
258 *fblock = phys_block;
259 }
260
261 if (block != NULL) {
262 block_put(block);
263 }
264
265 return EOK;
266}
267
268static int ext4_extent_find_extent(ext4_inode_ref_t *inode_ref,
269 uint32_t iblock, ext4_extent_path_t **ret_path)
270{
271 int rc;
272
273 ext4_extent_header_t *eh =
274 ext4_inode_get_extent_header(inode_ref->inode);
275
276 uint16_t depth = ext4_extent_header_get_depth(eh);
277
278 ext4_extent_path_t *tmp_path;
279
280 // Added 2 for possible tree growing
281 tmp_path = malloc(sizeof(ext4_extent_path_t) * (depth + 2));
282 if (tmp_path == NULL) {
283 return ENOMEM;
284 }
285
286 tmp_path[0].block = inode_ref->block;
287 tmp_path[0].header = eh;
288
289 uint16_t pos = 0;
290 while (ext4_extent_header_get_depth(eh) != 0) {
291
292 ext4_extent_binsearch_idx(tmp_path[pos].header, &tmp_path[pos].index, iblock);
293
294 tmp_path[pos].depth = depth;
295 tmp_path[pos].extent = NULL;
296
297 assert(tmp_path[pos].index != NULL);
298
299 uint64_t fblock = ext4_extent_index_get_leaf(tmp_path[pos].index);
300
301 block_t *block;
302 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NONE);
303 if (rc != EOK) {
304 goto cleanup;
305 }
306
307 pos++;
308
309 eh = (ext4_extent_header_t *)block->data;
310 tmp_path[pos].block = block;
311 tmp_path[pos].header = eh;
312
313 }
314
315 tmp_path[pos].depth = 0;
316 tmp_path[pos].extent = NULL;
317 tmp_path[pos].index = NULL;
318
319 /* find extent */
320 ext4_extent_binsearch(tmp_path[pos].header, &tmp_path[pos].extent, iblock);
321
322 *ret_path = tmp_path;
323
324 return EOK;
325
326cleanup:
327 // Put loaded blocks
328 // From 1 -> 0 is a block with inode data
329 for (uint16_t i = 1; i < tmp_path->depth; ++i) {
330 if (tmp_path[i].block) {
331 block_put(tmp_path[i].block);
332 }
333 }
334
335 // Destroy temporary data structure
336 free(tmp_path);
337
338 return rc;
339
340}
341
342static int ext4_extent_release(ext4_inode_ref_t *inode_ref, ext4_extent_t* extent)
343{
344 int rc;
345
346 uint64_t start = ext4_extent_get_start(extent);
347 uint16_t block_count = ext4_extent_get_block_count(extent);
348
349 rc = ext4_balloc_free_blocks(inode_ref, start, block_count);
350 if (rc != EOK) {
351 EXT4FS_DBG("ERROR");
352 return rc;
353 }
354
355 return EOK;
356}
357
358// Recursive release
359static int ext4_extent_release_branch(ext4_inode_ref_t *inode_ref,
360 ext4_extent_index_t *index)
361{
362 int rc;
363
364 block_t* block;
365
366 uint32_t fblock = ext4_extent_index_get_leaf(index);
367
368 EXT4FS_DBG("fblock = \%u", fblock);
369
370 rc = block_get(&block, inode_ref->fs->device, fblock, BLOCK_FLAGS_NOREAD);
371 if (rc != EOK) {
372 EXT4FS_DBG("ERROR get_block");
373 return rc;
374 }
375
376 ext4_extent_header_t *header = block->data;
377
378 if (ext4_extent_header_get_depth(header)) {
379
380 ext4_extent_index_t *idx = EXT4_EXTENT_FIRST_INDEX(header);
381
382 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++idx) {
383 rc = ext4_extent_release_branch(inode_ref, idx);
384 if (rc != EOK) {
385 EXT4FS_DBG("error recursion");
386 return rc;
387 }
388 }
389 } else {
390 ext4_extent_t *ext = EXT4_EXTENT_FIRST(header);
391
392 for (uint32_t i = 0; i < ext4_extent_header_get_entries_count(header); ++i, ++ext) {
393 rc = ext4_extent_release(inode_ref, ext);
394 if (rc != EOK) {
395 EXT4FS_DBG("error recursion");
396 return rc;
397 }
398 }
399 }
400
401 rc = block_put(block);
402 if (rc != EOK) {
403 EXT4FS_DBG("ERROR put_block");
404 return rc;
405 }
406
407 ext4_balloc_free_block(inode_ref, fblock);
408
409 return EOK;
410}
411
412int ext4_extent_release_blocks_from(ext4_inode_ref_t *inode_ref,
413 uint32_t iblock_from)
414{
415 int rc = EOK;
416
417 // Find the first extent to modify
418 ext4_extent_path_t *path;
419 rc = ext4_extent_find_extent(inode_ref, iblock_from, &path);
420 if (rc != EOK) {
421 return rc;
422 }
423
424 // Jump to last item of the path (extent)
425 ext4_extent_path_t *path_ptr = path;
426 while (path_ptr->depth != 0) {
427 path_ptr++;
428 }
429
430 assert(path_ptr->extent != NULL);
431
432 // First extent maybe released partially
433 uint32_t first_fblock;
434 first_fblock = ext4_extent_get_start(path_ptr->extent) + iblock_from;
435 first_fblock -= ext4_extent_get_first_block(path_ptr->extent);
436
437 uint16_t block_count = ext4_extent_get_block_count(path_ptr->extent);
438
439 uint16_t delete_count = block_count - first_fblock +
440 ext4_extent_get_start(path_ptr->extent);
441
442 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
443 if (rc != EOK) {
444 goto cleanup;
445 }
446
447 block_count -= delete_count;
448 ext4_extent_set_block_count(path_ptr->extent, block_count);
449
450 uint16_t entries = ext4_extent_header_get_entries_count(path_ptr->header);
451 ext4_extent_t *tmp_ext = path_ptr->extent + 1;
452 ext4_extent_t *stop_ext = EXT4_EXTENT_FIRST(path_ptr->header) + entries;
453
454 // If first extent empty, release it
455 if (block_count == 0) {
456 entries--;
457 ext4_extent_header_set_entries_count(path_ptr->header, entries);
458 }
459
460 // Release all successors of the first extent in the same node
461 while (tmp_ext < stop_ext) {
462 first_fblock = ext4_extent_get_start(tmp_ext);
463 delete_count = ext4_extent_get_block_count(tmp_ext);
464
465 rc = ext4_balloc_free_blocks(inode_ref, first_fblock, delete_count);
466 if (rc != EOK) {
467 goto cleanup;
468 }
469
470 entries--;
471 ext4_extent_header_set_entries_count(path_ptr->header, entries);
472
473 tmp_ext++;
474 }
475
476 // If leaf node is empty, the whole tree must be checked and the node will be released
477 bool check_tree = false;
478
479 // Don't release root block (including inode data) !!!
480 if ((path_ptr != path) && (entries == 0)) {
481 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
482 if (rc != EOK) {
483 goto cleanup;
484 }
485 check_tree = true;
486 }
487
488 // Jump to the parent
489 --path_ptr;
490
491 // release all successors in all levels
492 while(path_ptr >= path) {
493 entries = ext4_extent_header_get_entries_count(path_ptr->header);
494 ext4_extent_index_t *index = path_ptr->index + 1;
495 ext4_extent_index_t *stop =
496 EXT4_EXTENT_FIRST_INDEX(path_ptr->header) + entries;
497
498 if (check_tree) {
499 entries--;
500 ext4_extent_header_set_entries_count(path_ptr->header, entries);
501 }
502
503 while (index < stop) {
504 rc = ext4_extent_release_branch(inode_ref, index);
505 if (rc != EOK) {
506 goto cleanup;
507 }
508 ++index;
509 --entries;
510 ext4_extent_header_set_entries_count(path_ptr->header, entries);
511 }
512
513 path_ptr->block->dirty = true;
514
515 if ((entries == 0) && (path_ptr != path)) {
516 rc = ext4_balloc_free_block(inode_ref, path_ptr->block->lba);
517 if (rc != EOK) {
518 goto cleanup;
519 }
520 check_tree = true;
521 } else {
522 check_tree = false;
523 }
524
525 --path_ptr;
526 }
527
528
529cleanup:
530 // Put loaded blocks
531 // From 1 -> 0 is a block with inode data
532 for (uint16_t i = 1; i < path->depth; ++i) {
533 if (path[i].block) {
534 block_put(path[i].block);
535 }
536 }
537
538 // Destroy temporary data structure
539 free(path);
540
541 return rc;
542}
543
544int ext4_extent_append_block(ext4_inode_ref_t *inode_ref,
545 uint32_t *iblock, uint32_t *fblock)
546{
547 int rc = EOK;
548
549 ext4_superblock_t *sb = inode_ref->fs->superblock;
550 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
551
552 uint32_t block_size = ext4_superblock_get_block_size(sb);
553 uint32_t new_block_idx = inode_size / block_size;
554
555 ext4_extent_path_t *path;
556 rc = ext4_extent_find_extent(inode_ref, new_block_idx, &path);
557 if (rc != EOK) {
558 return rc;
559 }
560
561 // Jump to last item of the path (extent)
562 ext4_extent_path_t *path_ptr = path;
563 while (path_ptr->depth != 0) {
564 path_ptr++;
565 }
566
567 // if extent == NULL -> add extent to leaf
568 if (path_ptr->extent == NULL) {
569 ext4_extent_t *ext = EXT4_EXTENT_FIRST(path_ptr->header);
570 ext4_extent_set_block_count(ext, 0);
571
572 ext4_extent_header_set_entries_count(path_ptr->header, 1);
573
574 path_ptr->extent = ext;
575 }
576
577 uint32_t phys_block;
578
579 if (ext4_extent_get_block_count(path_ptr->extent) == 0) {
580
581 // Add first block to the extent
582
583 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
584 if (rc != EOK) {
585 goto finish;
586 }
587
588 ext4_extent_set_block_count(path_ptr->extent, 1);
589 ext4_extent_set_start(path_ptr->extent, phys_block);
590
591 path_ptr->block->dirty = true;
592
593 goto finish;
594
595 } else {
596 // try allocate scceeding extent block
597
598 // TODO
599 assert(false);
600
601 }
602
603
604finish:
605
606 *iblock = new_block_idx;
607 *fblock = phys_block;
608
609 // Put loaded blocks
610 // From 1 -> 0 is a block with inode data
611 for (uint16_t i = 1; i < path->depth; ++i) {
612 if (path[i].block) {
613 block_put(path[i].block);
614 }
615 }
616
617 // Destroy temporary data structure
618 free(path);
619
620 return rc;
621}
622
623/**
624 * @}
625 */
Note: See TracBrowser for help on using the repository browser.