source: mainline/uspace/lib/ext4/libext4_filesystem.c@ fffb061

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

find extent procedure with path saving (it's slow), for loading block will be probably used not saving variant

  • Property mode set to 100644
File size: 21.6 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_filesystem.c
35 * @brief More complex filesystem operations.
36 */
37
38#include <byteorder.h>
39#include <errno.h>
40#include <malloc.h>
41#include "libext4.h"
42
43int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
44{
45 int rc;
46
47 fs->device = service_id;
48
49 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
50 if (rc != EOK) {
51 return rc;
52 }
53
54 /* Read superblock from device */
55 ext4_superblock_t *temp_superblock;
56 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
57 if (rc != EOK) {
58 block_fini(fs->device);
59 return rc;
60 }
61
62 /* Read block size from superblock and check */
63 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
64 if (block_size > EXT4_MAX_BLOCK_SIZE) {
65 block_fini(fs->device);
66 return ENOTSUP;
67 }
68
69 /* Initialize block caching */
70 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
71 if (rc != EOK) {
72 block_fini(fs->device);
73 return rc;
74 }
75
76 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
77 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
78 fs->inode_blocks_per_level[0] = 1;
79 for (int i = 1; i < 4; i++) {
80 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
81 block_ids_per_block;
82 fs->inode_block_limits[i] = fs->inode_block_limits[i-1] +
83 fs->inode_blocks_per_level[i];
84 }
85
86 /* Return loaded superblock */
87 fs->superblock = temp_superblock;
88
89 return EOK;
90}
91
92int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
93{
94 int rc = EOK;
95
96 if (write_sb) {
97 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
98 }
99
100 free(fs->superblock);
101 block_fini(fs->device);
102
103 return rc;
104}
105
106int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
107{
108 int rc;
109
110 rc = ext4_superblock_check_sanity(fs->superblock);
111 if (rc != EOK) {
112 return rc;
113 }
114
115 return EOK;
116}
117
118int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *o_read_only)
119{
120 /* Feature flags are present in rev 1 and later */
121 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
122 *o_read_only = false;
123 return EOK;
124 }
125
126 uint32_t incompatible_features;
127 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
128 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
129 if (incompatible_features > 0) {
130 *o_read_only = true;
131 return ENOTSUP;
132 }
133
134 uint32_t compatible_read_only;
135 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
136 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
137 if (compatible_read_only > 0) {
138 *o_read_only = true;
139 }
140
141 return EOK;
142}
143
144int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
145 ext4_block_group_ref_t **ref)
146{
147 int rc;
148
149 ext4_block_group_ref_t *newref = malloc(sizeof(ext4_block_group_ref_t));
150 if (newref == NULL) {
151 return ENOMEM;
152 }
153
154 uint32_t descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
155 / ext4_superblock_get_desc_size(fs->superblock);
156
157 /* Block group descriptor table starts at the next block after superblock */
158 aoff64_t block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
159
160 /* Find the block containing the descriptor we are looking for */
161 block_id += bgid / descriptors_per_block;
162 uint32_t offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
163
164 rc = block_get(&newref->block, fs->device, block_id, 0);
165 if (rc != EOK) {
166 free(newref);
167 return rc;
168 }
169
170 newref->block_group = newref->block->data + offset;
171 newref->dirty = false;
172
173 *ref = newref;
174
175 return EOK;
176}
177
178int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
179{
180 int rc;
181
182 if (ref->dirty) {
183 ref->block->dirty = true;
184 }
185
186 rc = block_put(ref->block);
187 free(ref);
188
189 return rc;
190}
191
192int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
193 ext4_inode_ref_t **ref)
194{
195 int rc;
196
197 ext4_inode_ref_t *newref = malloc(sizeof(ext4_inode_ref_t));
198 if (newref == NULL) {
199 return ENOMEM;
200 }
201
202 uint32_t inodes_per_group =
203 ext4_superblock_get_inodes_per_group(fs->superblock);
204
205 /* inode numbers are 1-based, but it is simpler to work with 0-based
206 * when computing indices
207 */
208 index -= 1;
209 uint32_t block_group = index / inodes_per_group;
210 uint32_t offset_in_group = index % inodes_per_group;
211
212 ext4_block_group_ref_t *bg_ref;
213 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
214 if (rc != EOK) {
215 free(newref);
216 return rc;
217 }
218
219 uint32_t inode_table_start = ext4_block_group_get_inode_table_first_block(
220 bg_ref->block_group, fs->superblock);
221
222 rc = ext4_filesystem_put_block_group_ref(bg_ref);
223 if (rc != EOK) {
224 free(newref);
225 return rc;
226 }
227
228 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
229 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
230 uint32_t byte_offset_in_group = offset_in_group * inode_size;
231
232 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
233 rc = block_get(&newref->block, fs->device, block_id, 0);
234 if (rc != EOK) {
235 free(newref);
236 return rc;
237 }
238
239 uint32_t offset_in_block = byte_offset_in_group % block_size;
240 newref->inode = newref->block->data + offset_in_block;
241 /* we decremented index above, but need to store the original value
242 * in the reference
243 */
244 newref->index = index+1;
245 newref->dirty = false;
246
247 *ref = newref;
248
249 return EOK;
250}
251
252
253int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
254{
255 int rc;
256
257 if (ref->dirty) {
258 ref->block->dirty = true;
259 }
260
261 rc = block_put(ref->block);
262 free(ref);
263
264 return rc;
265}
266
267int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
268 ext4_inode_ref_t **inode_ref, int flags)
269{
270 int rc;
271
272 bool is_dir = false;
273 if (flags & L_DIRECTORY) {
274 is_dir = true;
275 }
276
277 // allocate inode
278 uint32_t index;
279 rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
280 if (rc != EOK) {
281 return rc;
282 }
283
284 // TODO extents, dir_index etc...
285
286 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
287 if (rc != EOK) {
288 ext4_ialloc_free_inode(fs, index, is_dir);
289 return rc;
290 }
291
292 // init inode
293 ext4_inode_t *inode = (*inode_ref)->inode;
294
295 if (is_dir) {
296 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_DIRECTORY);
297 ext4_inode_set_links_count(inode, 1); // '.' entry
298 } else {
299 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_FILE);
300 ext4_inode_set_links_count(inode, 0);
301 }
302
303 ext4_inode_set_uid(inode, 0);
304 ext4_inode_set_gid(inode, 0);
305 ext4_inode_set_size(inode, 0);
306 ext4_inode_set_access_time(inode, 0);
307 ext4_inode_set_change_inode_time(inode, 0);
308 ext4_inode_set_modification_time(inode, 0);
309 ext4_inode_set_deletion_time(inode, 0);
310 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
311 ext4_inode_set_flags(inode, 0);
312 ext4_inode_set_generation(inode, 0);
313
314 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++) {
315 inode->blocks[i] = 0;
316 }
317
318 (*inode_ref)->dirty = true;
319
320 return EOK;
321}
322
323int ext4_filesystem_free_inode(ext4_filesystem_t *fs, ext4_inode_ref_t *inode_ref)
324{
325 int rc;
326
327 // release all indirect (no data) blocks
328
329 // 1) Single indirect
330 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
331 if (fblock != 0) {
332 rc = ext4_balloc_free_block(fs, inode_ref, fblock);
333 if (rc != EOK) {
334 return rc;
335 }
336
337 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
338 }
339
340 block_t *block;
341 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
342 uint32_t count = block_size / sizeof(uint32_t);
343
344 // 2) Double indirect
345 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
346 if (fblock != 0) {
347 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
348 if (rc != EOK) {
349 return rc;
350 }
351
352 uint32_t ind_block;
353 for (uint32_t offset = 0; offset < count; ++offset) {
354 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
355
356 if (ind_block != 0) {
357 rc = ext4_balloc_free_block(fs, inode_ref, ind_block);
358 if (rc != EOK) {
359 block_put(block);
360 return rc;
361 }
362 }
363 }
364
365 block_put(block);
366 rc = ext4_balloc_free_block(fs, inode_ref, fblock);
367 if (rc != EOK) {
368 return rc;
369 }
370
371 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
372 }
373
374
375 // 3) Tripple indirect
376 block_t *subblock;
377 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
378 if (fblock != 0) {
379 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
380 if (rc != EOK) {
381 return rc;
382 }
383
384 uint32_t ind_block;
385 for (uint32_t offset = 0; offset < count; ++offset) {
386 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
387
388 if (ind_block != 0) {
389 rc = block_get(&subblock, fs->device, ind_block, BLOCK_FLAGS_NONE);
390 if (rc != EOK) {
391 block_put(block);
392 return rc;
393 }
394
395 uint32_t ind_subblock;
396 for (uint32_t suboffset = 0; suboffset < count; ++suboffset) {
397 ind_subblock = uint32_t_le2host(((uint32_t*)subblock->data)[suboffset]);
398
399 if (ind_subblock != 0) {
400 rc = ext4_balloc_free_block(fs, inode_ref, ind_subblock);
401 if (rc != EOK) {
402 block_put(subblock);
403 block_put(block);
404 return rc;
405 }
406 }
407
408 }
409 block_put(subblock);
410
411 }
412
413 rc = ext4_balloc_free_block(fs, inode_ref, ind_block);
414 if (rc != EOK) {
415 block_put(block);
416 return rc;
417 }
418
419
420 }
421
422 block_put(block);
423 rc = ext4_balloc_free_block(fs, inode_ref, fblock);
424 if (rc != EOK) {
425 return rc;
426 }
427
428 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
429 }
430
431 inode_ref->dirty = true;
432
433 // Free inode
434 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
435 EXT4_INODE_MODE_DIRECTORY)) {
436 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
437 } else {
438 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
439 }
440 if (rc != EOK) {
441 return rc;
442 }
443
444 return EOK;
445}
446
447int ext4_filesystem_truncate_inode(ext4_filesystem_t *fs,
448 ext4_inode_ref_t *inode_ref, aoff64_t new_size)
449{
450 int rc;
451
452 if (! ext4_inode_can_truncate(fs->superblock, inode_ref->inode)) {
453 // Unable to truncate
454 return EINVAL;
455 }
456
457 aoff64_t old_size = ext4_inode_get_size(fs->superblock, inode_ref->inode);
458 if (old_size == new_size) {
459 // Nothing to do
460 return EOK;
461 }
462
463 // It's not suppported to make the larger file
464 if (old_size < new_size) {
465 return EINVAL;
466 }
467
468 aoff64_t size_diff = old_size - new_size;
469 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
470 uint32_t diff_blocks_count = size_diff / block_size;
471 if (size_diff % block_size != 0) {
472 diff_blocks_count++;
473 }
474
475 uint32_t old_blocks_count = old_size / block_size;
476 if (old_size % block_size != 0) {
477 old_blocks_count++;
478 }
479
480 // starting from 1 because of logical blocks are numbered from 0
481 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
482 rc = ext4_filesystem_release_inode_block(fs, inode_ref, old_blocks_count - i);
483 if (rc != EOK) {
484 return rc;
485 }
486 }
487
488 ext4_inode_set_size(inode_ref->inode, new_size);
489
490 inode_ref->dirty = true;
491
492 return EOK;
493}
494
495int ext4_filesystem_get_inode_data_block_index(ext4_filesystem_t *fs,
496 ext4_inode_ref_t *inode_ref, aoff64_t iblock, uint32_t* fblock)
497{
498 int rc;
499
500
501 uint32_t current_block;
502
503 /* Handle inode using extents */
504 if (ext4_superblock_has_feature_incompatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
505 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
506 rc = ext4_extent_find_block(fs, inode_ref, iblock, &current_block);
507
508 if (rc != EOK) {
509 return rc;
510 }
511
512 *fblock = current_block;
513 return EOK;
514
515 }
516
517 ext4_inode_t *inode = inode_ref->inode;
518
519 /* Handle simple case when we are dealing with direct reference */
520 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
521 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
522 *fblock = current_block;
523 return EOK;
524 }
525
526 /* Determine the indirection level needed to get the desired block */
527 int level = -1;
528 for (int i = 1; i < 4; i++) {
529 if (iblock < fs->inode_block_limits[i]) {
530 level = i;
531 break;
532 }
533 }
534
535 if (level == -1) {
536 return EIO;
537 }
538
539 /* Compute offsets for the topmost level */
540 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
541 current_block = ext4_inode_get_indirect_block(inode, level-1);
542 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
543
544 if (current_block == 0) {
545 *fblock = 0;
546 return EOK;
547 }
548
549 block_t *block;
550
551 /* Navigate through other levels, until we find the block number
552 * or find null reference meaning we are dealing with sparse file
553 */
554 while (level > 0) {
555 rc = block_get(&block, fs->device, current_block, 0);
556 if (rc != EOK) {
557 return rc;
558 }
559
560 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
561
562 rc = block_put(block);
563 if (rc != EOK) {
564 return rc;
565 }
566
567 if (current_block == 0) {
568 /* This is a sparse file */
569 *fblock = 0;
570 return EOK;
571 }
572
573 level -= 1;
574
575 /* If we are on the last level, break here as
576 * there is no next level to visit
577 */
578 if (level == 0) {
579 break;
580 }
581
582 /* Visit the next level */
583 block_offset_in_level %= fs->inode_blocks_per_level[level];
584 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
585 }
586
587 *fblock = current_block;
588
589 return EOK;
590}
591
592
593int ext4_filesystem_set_inode_data_block_index(ext4_filesystem_t *fs,
594 ext4_inode_ref_t *inode_ref, aoff64_t iblock, uint32_t fblock)
595{
596 int rc;
597
598
599 /* Handle inode using extents */
600 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
601 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
602 // TODO
603 return ENOTSUP;
604 }
605
606 /* Handle simple case when we are dealing with direct reference */
607 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
608 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
609 inode_ref->dirty = true;
610 return EOK;
611 }
612
613 /* Determine the indirection level needed to get the desired block */
614 int level = -1;
615 for (int i = 1; i < 4; i++) {
616 if (iblock < fs->inode_block_limits[i]) {
617 level = i;
618 break;
619 }
620 }
621
622 if (level == -1) {
623 return EIO;
624 }
625
626 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
627
628 /* Compute offsets for the topmost level */
629 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
630 uint32_t current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
631 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
632
633 uint32_t new_block_addr;
634 block_t *block, *new_block;
635
636 if (current_block == 0) {
637 rc = ext4_balloc_alloc_block(fs, inode_ref, &new_block_addr);
638 if (rc != EOK) {
639 return rc;
640 }
641
642 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
643
644 inode_ref->dirty = true;
645
646 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
647 if (rc != EOK) {
648 ext4_balloc_free_block(fs, inode_ref, new_block_addr);
649 return rc;
650 }
651
652 memset(new_block->data, 0, block_size);
653 new_block->dirty = true;
654
655 rc = block_put(new_block);
656 if (rc != EOK) {
657 return rc;
658 }
659
660 current_block = new_block_addr;
661 }
662
663 /* Navigate through other levels, until we find the block number
664 * or find null reference meaning we are dealing with sparse file
665 */
666 while (level > 0) {
667
668 rc = block_get(&block, fs->device, current_block, 0);
669 if (rc != EOK) {
670 return rc;
671 }
672
673 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
674
675 if ((level > 1) && (current_block == 0)) {
676 rc = ext4_balloc_alloc_block(fs, inode_ref, &new_block_addr);
677 if (rc != EOK) {
678 block_put(block);
679 return rc;
680 }
681
682 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
683 if (rc != EOK) {
684 block_put(block);
685 return rc;
686 }
687
688 memset(new_block->data, 0, block_size);
689 new_block->dirty = true;
690
691 rc = block_put(new_block);
692 if (rc != EOK) {
693 block_put(block);
694 return rc;
695 }
696
697 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
698 block->dirty = true;
699 current_block = new_block_addr;
700 }
701
702 if (level == 1) {
703 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
704 block->dirty = true;
705 }
706
707 rc = block_put(block);
708 if (rc != EOK) {
709 return rc;
710 }
711
712 level -= 1;
713
714 /* If we are on the last level, break here as
715 * there is no next level to visit
716 */
717 if (level == 0) {
718 break;
719 }
720
721 /* Visit the next level */
722 block_offset_in_level %= fs->inode_blocks_per_level[level];
723 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
724 }
725
726 return EOK;
727}
728
729int ext4_filesystem_release_inode_block(ext4_filesystem_t *fs,
730 ext4_inode_ref_t *inode_ref, uint32_t iblock)
731{
732 int rc;
733
734 uint32_t fblock;
735
736 /* TODO Handle extents */
737// if (ext4_superblock_has_feature_incompatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
738// ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
739//// rc = ext4_extent_find_block(fs, inode_ref, iblock, &current_block);
740////
741//// if (rc != EOK) {
742//// return rc;
743//// }
744////
745//// *fblock = current_block;
746//
747// // TODO release block from extents (and return fblock)
748//
749// fblock = 0;
750//
751// if (fblock == 0) {
752// return EOK;
753// }
754//
755// return ext4_balloc_free_block(fs, inode_ref, fblock);
756//
757// return EOK;
758//
759// }
760
761
762
763 ext4_inode_t *inode = inode_ref->inode;
764
765 /* Handle simple case when we are dealing with direct reference */
766 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
767 fblock = ext4_inode_get_direct_block(inode, iblock);
768 // Sparse file
769 if (fblock == 0) {
770 return EOK;
771 }
772
773 ext4_inode_set_direct_block(inode, iblock, 0);
774 return ext4_balloc_free_block(fs, inode_ref, fblock);
775 }
776
777
778 /* Determine the indirection level needed to get the desired block */
779 int level = -1;
780 for (int i = 1; i < 4; i++) {
781 if (iblock < fs->inode_block_limits[i]) {
782 level = i;
783 break;
784 }
785 }
786
787 if (level == -1) {
788 return EIO;
789 }
790
791 /* Compute offsets for the topmost level */
792 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
793 uint32_t current_block = ext4_inode_get_indirect_block(inode, level-1);
794 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
795
796 /* Navigate through other levels, until we find the block number
797 * or find null reference meaning we are dealing with sparse file
798 */
799 block_t *block;
800 while (level > 0) {
801 rc = block_get(&block, fs->device, current_block, 0);
802 if (rc != EOK) {
803 return rc;
804 }
805
806 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
807
808 // Set zero
809 if (level == 1) {
810 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
811 block->dirty = true;
812 }
813
814 rc = block_put(block);
815 if (rc != EOK) {
816 return rc;
817 }
818
819 level -= 1;
820
821 /* If we are on the last level, break here as
822 * there is no next level to visit
823 */
824 if (level == 0) {
825 break;
826 }
827
828 /* Visit the next level */
829 block_offset_in_level %= fs->inode_blocks_per_level[level];
830 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
831 }
832
833 fblock = current_block;
834
835 if (fblock == 0) {
836 return EOK;
837 }
838
839 return ext4_balloc_free_block(fs, inode_ref, fblock);
840
841}
842
843int ext4_filesystem_add_orphan(ext4_filesystem_t *fs,
844 ext4_inode_ref_t *inode_ref)
845{
846 uint32_t next_orphan = ext4_superblock_get_last_orphan(fs->superblock);
847 ext4_inode_set_deletion_time(inode_ref->inode, next_orphan);
848 ext4_superblock_set_last_orphan(fs->superblock, inode_ref->index);
849 inode_ref->dirty = true;
850
851 return EOK;
852}
853
854int ext4_filesystem_delete_orphan(ext4_filesystem_t *fs,
855 ext4_inode_ref_t *inode_ref)
856{
857 int rc;
858
859 uint32_t last_orphan = ext4_superblock_get_last_orphan(fs->superblock);
860 assert(last_orphan > 0);
861
862 uint32_t next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
863
864 if (last_orphan == inode_ref->index) {
865 ext4_superblock_set_last_orphan(fs->superblock, next_orphan);
866 ext4_inode_set_deletion_time(inode_ref->inode, 0);
867 inode_ref->dirty = true;
868 return EOK;
869 }
870
871 ext4_inode_ref_t *current;
872 rc = ext4_filesystem_get_inode_ref(fs, last_orphan, &current);
873 if (rc != EOK) {
874 return rc;
875 }
876 next_orphan = ext4_inode_get_deletion_time(current->inode);
877
878 bool found;
879
880 while (next_orphan != 0) {
881 if (next_orphan == inode_ref->index) {
882 next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
883 ext4_inode_set_deletion_time(current->inode, next_orphan);
884 current->dirty = true;
885 found = true;
886 break;
887 }
888
889 ext4_filesystem_put_inode_ref(current);
890
891 rc = ext4_filesystem_get_inode_ref(fs, next_orphan, &current);
892 if (rc != EOK) {
893 return rc;
894 }
895 next_orphan = ext4_inode_get_deletion_time(current->inode);
896
897 }
898
899 ext4_inode_set_deletion_time(inode_ref->inode, 0);
900
901 rc = ext4_filesystem_put_inode_ref(current);
902 if (rc != EOK) {
903 return rc;
904 }
905
906 if (!found) {
907 return ENOENT;
908 }
909
910 return EOK;
911}
912
913/**
914 * @}
915 */
Note: See TracBrowser for help on using the repository browser.