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

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

More comments - mostly by filesystem operations

  • Property mode set to 100644
File size: 27.7 KB
Line 
1/*
2 * Copyright (c) 2012 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
43/** Initialize filesystem and read all needed data.
44 *
45 * @param fs filesystem instance to be initialized
46 * @param service_id identifier if device with the filesystem
47 * @return error code
48 */
49int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id)
50{
51 int rc;
52
53 fs->device = service_id;
54
55 // Initialize block library (4096 is size of communication channel)
56 rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
57 if (rc != EOK) {
58 return rc;
59 }
60
61 // Read superblock from device to memory
62 ext4_superblock_t *temp_superblock;
63 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
64 if (rc != EOK) {
65 block_fini(fs->device);
66 return rc;
67 }
68
69 // Read block size from superblock and check
70 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
71 if (block_size > EXT4_MAX_BLOCK_SIZE) {
72 block_fini(fs->device);
73 return ENOTSUP;
74 }
75
76 // Initialize block caching by libblock
77 rc = block_cache_init(service_id, block_size, 0, CACHE_MODE_WT);
78 if (rc != EOK) {
79 block_fini(fs->device);
80 return rc;
81 }
82
83 // Compute limits for indirect block levels
84 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
85 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
86 fs->inode_blocks_per_level[0] = 1;
87 for (int i = 1; i < 4; i++) {
88 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i-1] *
89 block_ids_per_block;
90 fs->inode_block_limits[i] = fs->inode_block_limits[i-1] +
91 fs->inode_blocks_per_level[i];
92 }
93
94 // Return loaded superblock
95 fs->superblock = temp_superblock;
96
97 return EOK;
98}
99
100/** Destroy filesystem instance (used by unmount operation).
101 *
102 * @param fs filesystem to be destroyed
103 * @param write_sb flag if superblock should be written to device
104 * @return error code
105 */
106int ext4_filesystem_fini(ext4_filesystem_t *fs, bool write_sb)
107{
108 int rc = EOK;
109
110 // If needed, write the superblock to the device
111 if (write_sb) {
112 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
113 }
114
115 // Release memory space for superblock
116 free(fs->superblock);
117
118 // Finish work with block library
119 block_fini(fs->device);
120
121 return rc;
122}
123
124/** Check sanity of the filesystem.
125 *
126 * Main is the check of the superblock structure.
127 *
128 * @param fs filesystem to be checked
129 * @return error code
130 */
131int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
132{
133 int rc;
134
135 // Check superblock
136 rc = ext4_superblock_check_sanity(fs->superblock);
137 if (rc != EOK) {
138 return rc;
139 }
140
141 return EOK;
142}
143
144/** Check filesystem's features, if supported by this driver
145 *
146 * Function can return EOK and set read_only flag. It mean's that
147 * there are some not-supported features, that can cause problems
148 * during some write operations.
149 *
150 * @param fs filesystem to be checked
151 * @param read_only flag if filesystem should be mounted only for reading
152 * @return error code
153 */
154int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *read_only)
155{
156 // Feature flags are present only in higher revisions
157 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
158 *read_only = false;
159 return EOK;
160 }
161
162 // Check incompatible features - if filesystem has some,
163 // volume can't be mounted
164 uint32_t incompatible_features;
165 incompatible_features = ext4_superblock_get_features_incompatible(fs->superblock);
166 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
167 if (incompatible_features > 0) {
168 return ENOTSUP;
169 }
170
171 // Check read-only features, if filesystem has some,
172 // volume can be mount only in read-only mode
173 uint32_t compatible_read_only;
174 compatible_read_only = ext4_superblock_get_features_read_only(fs->superblock);
175 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
176 if (compatible_read_only > 0) {
177 *read_only = true;
178 return EOK;
179 }
180
181 return EOK;
182}
183
184/** Get reference to block group specified by index.
185 *
186 * @param fs filesystem to find block group on
187 * @param bgid index of block group to load
188 * @oaram ref output pointer for reference
189 * @return error code
190 */
191int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
192 ext4_block_group_ref_t **ref)
193{
194 int rc;
195
196 // Allocate memory for new structure
197 ext4_block_group_ref_t *newref = malloc(sizeof(ext4_block_group_ref_t));
198 if (newref == NULL) {
199 return ENOMEM;
200 }
201
202 // Compute number of descriptors, that fits in one data block
203 uint32_t descriptors_per_block = ext4_superblock_get_block_size(fs->superblock)
204 / ext4_superblock_get_desc_size(fs->superblock);
205
206 // Block group descriptor table starts at the next block after superblock
207 aoff64_t block_id = ext4_superblock_get_first_data_block(fs->superblock) + 1;
208
209 // Find the block containing the descriptor we are looking for
210 block_id += bgid / descriptors_per_block;
211 uint32_t offset = (bgid % descriptors_per_block) * ext4_superblock_get_desc_size(fs->superblock);
212
213 // Load block with descriptors
214 rc = block_get(&newref->block, fs->device, block_id, 0);
215 if (rc != EOK) {
216 free(newref);
217 return rc;
218 }
219
220 // Inititialize in-memory representation
221 newref->block_group = newref->block->data + offset;
222 newref->fs = fs;
223 newref->index = bgid;
224 newref->dirty = false;
225
226 *ref = newref;
227
228 return EOK;
229}
230
231/** TODO comment
232 *
233 */
234static uint16_t ext4_filesystem_bg_checksum(ext4_superblock_t *sb, uint32_t bgid,
235 ext4_block_group_t *bg)
236{
237 uint16_t crc = 0;
238
239 if (ext4_superblock_has_feature_read_only(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
240
241 void *base = bg;
242 void *checksum = &bg->checksum;
243
244 uint32_t offset = (uint32_t)(checksum - base);
245
246 uint32_t le_group = host2uint32_t_le(bgid);
247
248 crc = crc16(~0, sb->uuid, sizeof(sb->uuid));
249 crc = crc16(crc, (uint8_t *)&le_group, sizeof(le_group));
250 crc = crc16(crc, (uint8_t *)bg, offset);
251
252 offset += sizeof(bg->checksum); /* skip checksum */
253
254 /* for checksum of struct ext4_group_desc do the rest...*/
255 if ((ext4_superblock_has_feature_incompatible(sb, EXT4_FEATURE_INCOMPAT_64BIT)) &&
256 offset < ext4_superblock_get_desc_size(sb)) {
257
258 crc = crc16(crc, ((uint8_t *)bg) + offset, ext4_superblock_get_desc_size(sb) - offset);
259 }
260 }
261
262 return crc;
263
264}
265
266/** Put reference to block group.
267 *
268 * @oaram ref pointer for reference to be put back
269 * @return error code
270 */
271int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
272{
273 int rc;
274
275 // Check if reference modified
276 if (ref->dirty) {
277
278 // Compute new checksum of block group
279 uint16_t checksum = ext4_filesystem_bg_checksum(
280 ref->fs->superblock, ref->index, ref->block_group);
281 ext4_block_group_set_checksum(ref->block_group, checksum);
282
283 // Mark block dirty for writing changes to physical device
284 ref->block->dirty = true;
285 }
286
287 // Put back block, that contains block group descriptor
288 rc = block_put(ref->block);
289 free(ref);
290
291 return rc;
292}
293
294/** Get reference to i-node specified by index.
295 *
296 * @param fs filesystem to find i-node on
297 * @param index index of i-node to load
298 * @oaram ref output pointer for reference
299 * @return error code
300 */
301int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
302 ext4_inode_ref_t **ref)
303{
304 int rc;
305
306 // Allocate memory for new structure
307 ext4_inode_ref_t *newref = malloc(sizeof(ext4_inode_ref_t));
308 if (newref == NULL) {
309 return ENOMEM;
310 }
311
312 // Compute number of i-nodes, that fits in one data block
313 uint32_t inodes_per_group =
314 ext4_superblock_get_inodes_per_group(fs->superblock);
315
316 /*
317 * inode numbers are 1-based, but it is simpler to work with 0-based
318 * when computing indices
319 */
320 index -= 1;
321 uint32_t block_group = index / inodes_per_group;
322 uint32_t offset_in_group = index % inodes_per_group;
323
324 // Load block group, where i-node is located
325 ext4_block_group_ref_t *bg_ref;
326 rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
327 if (rc != EOK) {
328 free(newref);
329 return rc;
330 }
331
332 // Load block address, where i-node table is located
333 uint32_t inode_table_start = ext4_block_group_get_inode_table_first_block(
334 bg_ref->block_group, fs->superblock);
335
336 // Put back block group reference (not needed more)
337 rc = ext4_filesystem_put_block_group_ref(bg_ref);
338 if (rc != EOK) {
339 free(newref);
340 return rc;
341 }
342
343 // Compute position of i-node in the block group
344 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
345 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
346 uint32_t byte_offset_in_group = offset_in_group * inode_size;
347
348 // Compute block address
349 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
350 rc = block_get(&newref->block, fs->device, block_id, 0);
351 if (rc != EOK) {
352 free(newref);
353 return rc;
354 }
355
356 // Compute position of i-node in the data block
357 uint32_t offset_in_block = byte_offset_in_group % block_size;
358 newref->inode = newref->block->data + offset_in_block;
359
360 // We need to store the original value of index in the reference
361 newref->index = index + 1;
362 newref->fs = fs;
363 newref->dirty = false;
364
365 *ref = newref;
366
367 return EOK;
368}
369
370/** TODO comment
371 *
372 */
373int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
374{
375 int rc;
376
377 if (ref->dirty) {
378 ref->block->dirty = true;
379 }
380
381 rc = block_put(ref->block);
382 free(ref);
383
384 return rc;
385}
386
387/** TODO comment
388 *
389 */
390int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
391 ext4_inode_ref_t **inode_ref, int flags)
392{
393 int rc;
394
395 bool is_dir = false;
396 if (flags & L_DIRECTORY) {
397 is_dir = true;
398 }
399
400 // allocate inode
401 uint32_t index;
402 rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
403 if (rc != EOK) {
404 return rc;
405 }
406
407 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
408 if (rc != EOK) {
409 ext4_ialloc_free_inode(fs, index, is_dir);
410 return rc;
411 }
412
413 // init inode
414 ext4_inode_t *inode = (*inode_ref)->inode;
415
416 if (is_dir) {
417 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_DIRECTORY);
418 ext4_inode_set_links_count(inode, 1); // '.' entry
419 } else {
420 ext4_inode_set_mode(fs->superblock, inode, EXT4_INODE_MODE_FILE);
421 ext4_inode_set_links_count(inode, 0);
422 }
423
424 ext4_inode_set_uid(inode, 0);
425 ext4_inode_set_gid(inode, 0);
426 ext4_inode_set_size(inode, 0);
427 ext4_inode_set_access_time(inode, 0);
428 ext4_inode_set_change_inode_time(inode, 0);
429 ext4_inode_set_modification_time(inode, 0);
430 ext4_inode_set_deletion_time(inode, 0);
431 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
432 ext4_inode_set_flags(inode, 0);
433 ext4_inode_set_generation(inode, 0);
434
435 // Reset blocks array
436 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++) {
437 inode->blocks[i] = 0;
438 }
439
440 if (ext4_superblock_has_feature_incompatible(
441 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
442
443 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
444
445 // Initialize extent root header
446 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
447 ext4_extent_header_set_depth(header, 0);
448 ext4_extent_header_set_entries_count(header, 0);
449 ext4_extent_header_set_generation(header, 0);
450 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
451
452 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof (uint32_t) - sizeof(ext4_extent_header_t))
453 / sizeof(ext4_extent_t);
454
455 ext4_extent_header_set_max_entries_count(header, max_entries);
456 }
457
458 (*inode_ref)->dirty = true;
459
460 return EOK;
461}
462
463/** TODO comment
464 *
465 */
466int ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
467{
468 int rc;
469
470 ext4_filesystem_t *fs = inode_ref->fs;
471
472 if (ext4_superblock_has_feature_incompatible(
473 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
474 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
475
476 // Data structures are released during truncate operation...
477 goto finish;
478 }
479
480 // release all indirect (no data) blocks
481
482 // 1) Single indirect
483 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
484 if (fblock != 0) {
485 rc = ext4_balloc_free_block(inode_ref, fblock);
486 if (rc != EOK) {
487 return rc;
488 }
489
490 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
491 }
492
493 block_t *block;
494 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
495 uint32_t count = block_size / sizeof(uint32_t);
496
497 // 2) Double indirect
498 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
499 if (fblock != 0) {
500 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
501 if (rc != EOK) {
502 return rc;
503 }
504
505 uint32_t ind_block;
506 for (uint32_t offset = 0; offset < count; ++offset) {
507 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
508
509 if (ind_block != 0) {
510 rc = ext4_balloc_free_block(inode_ref, ind_block);
511 if (rc != EOK) {
512 block_put(block);
513 return rc;
514 }
515 }
516 }
517
518 block_put(block);
519 rc = ext4_balloc_free_block(inode_ref, fblock);
520 if (rc != EOK) {
521 return rc;
522 }
523
524 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
525 }
526
527
528 // 3) Tripple indirect
529 block_t *subblock;
530 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
531 if (fblock != 0) {
532 rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
533 if (rc != EOK) {
534 return rc;
535 }
536
537 uint32_t ind_block;
538 for (uint32_t offset = 0; offset < count; ++offset) {
539 ind_block = uint32_t_le2host(((uint32_t*)block->data)[offset]);
540
541 if (ind_block != 0) {
542 rc = block_get(&subblock, fs->device, ind_block, BLOCK_FLAGS_NONE);
543 if (rc != EOK) {
544 block_put(block);
545 return rc;
546 }
547
548 uint32_t ind_subblock;
549 for (uint32_t suboffset = 0; suboffset < count; ++suboffset) {
550 ind_subblock = uint32_t_le2host(((uint32_t*)subblock->data)[suboffset]);
551
552 if (ind_subblock != 0) {
553 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
554 if (rc != EOK) {
555 block_put(subblock);
556 block_put(block);
557 return rc;
558 }
559 }
560
561 }
562 block_put(subblock);
563
564 }
565
566 rc = ext4_balloc_free_block(inode_ref, ind_block);
567 if (rc != EOK) {
568 block_put(block);
569 return rc;
570 }
571
572
573 }
574
575 block_put(block);
576 rc = ext4_balloc_free_block(inode_ref, fblock);
577 if (rc != EOK) {
578 return rc;
579 }
580
581 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
582 }
583
584finish:
585 inode_ref->dirty = true;
586
587 // Free inode
588 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
589 EXT4_INODE_MODE_DIRECTORY)) {
590 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
591 } else {
592 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
593 }
594 if (rc != EOK) {
595 return rc;
596 }
597
598 return EOK;
599}
600
601/** TODO comment
602 *
603 */
604int ext4_filesystem_truncate_inode(
605 ext4_inode_ref_t *inode_ref, aoff64_t new_size)
606{
607 int rc;
608
609 ext4_superblock_t *sb = inode_ref->fs->superblock;
610
611 if (! ext4_inode_can_truncate(sb, inode_ref->inode)) {
612 // Unable to truncate
613 return EINVAL;
614 }
615
616 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
617 if (old_size == new_size) {
618 // Nothing to do
619 return EOK;
620 }
621
622 // It's not suppported to make the larger file
623 if (old_size < new_size) {
624 return EINVAL;
625 }
626
627 aoff64_t size_diff = old_size - new_size;
628 uint32_t block_size = ext4_superblock_get_block_size(sb);
629 uint32_t diff_blocks_count = size_diff / block_size;
630 if (size_diff % block_size != 0) {
631 diff_blocks_count++;
632 }
633
634 uint32_t old_blocks_count = old_size / block_size;
635 if (old_size % block_size != 0) {
636 old_blocks_count++;
637 }
638
639 if (ext4_superblock_has_feature_incompatible(
640 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
641 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
642
643 rc = ext4_extent_release_blocks_from(inode_ref,
644 old_blocks_count - diff_blocks_count);
645 if (rc != EOK) {
646 return rc;
647 }
648 } else {
649 // starting from 1 because of logical blocks are numbered from 0
650 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
651 rc = ext4_filesystem_release_inode_block(inode_ref, old_blocks_count - i);
652 if (rc != EOK) {
653 return rc;
654 }
655 }
656 }
657
658 ext4_inode_set_size(inode_ref->inode, new_size);
659
660 inode_ref->dirty = true;
661
662 return EOK;
663}
664
665/** TODO comment
666 *
667 */
668int ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
669 aoff64_t iblock, uint32_t *fblock)
670{
671 int rc;
672
673 ext4_filesystem_t *fs = inode_ref->fs;
674
675 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
676 *fblock = 0;
677 return EOK;
678 }
679
680 uint32_t current_block;
681
682 /* Handle inode using extents */
683 if (ext4_superblock_has_feature_incompatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
684 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
685 rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
686
687 if (rc != EOK) {
688 return rc;
689 }
690
691 *fblock = current_block;
692 return EOK;
693
694 }
695
696 ext4_inode_t *inode = inode_ref->inode;
697
698 /* Handle simple case when we are dealing with direct reference */
699 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
700 current_block = ext4_inode_get_direct_block(inode, (uint32_t)iblock);
701 *fblock = current_block;
702 return EOK;
703 }
704
705 /* Determine the indirection level needed to get the desired block */
706 int level = -1;
707 for (int i = 1; i < 4; i++) {
708 if (iblock < fs->inode_block_limits[i]) {
709 level = i;
710 break;
711 }
712 }
713
714 if (level == -1) {
715 return EIO;
716 }
717
718 /* Compute offsets for the topmost level */
719 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
720 current_block = ext4_inode_get_indirect_block(inode, level-1);
721 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
722
723 if (current_block == 0) {
724 *fblock = 0;
725 return EOK;
726 }
727
728 block_t *block;
729
730 /* Navigate through other levels, until we find the block number
731 * or find null reference meaning we are dealing with sparse file
732 */
733 while (level > 0) {
734 rc = block_get(&block, fs->device, current_block, 0);
735 if (rc != EOK) {
736 return rc;
737 }
738
739 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
740
741 rc = block_put(block);
742 if (rc != EOK) {
743 return rc;
744 }
745
746 if (current_block == 0) {
747 /* This is a sparse file */
748 *fblock = 0;
749 return EOK;
750 }
751
752 level -= 1;
753
754 /* If we are on the last level, break here as
755 * there is no next level to visit
756 */
757 if (level == 0) {
758 break;
759 }
760
761 /* Visit the next level */
762 block_offset_in_level %= fs->inode_blocks_per_level[level];
763 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
764 }
765
766 *fblock = current_block;
767
768 return EOK;
769}
770
771/** TODO comment
772 *
773 */
774int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
775 aoff64_t iblock, uint32_t fblock)
776{
777 int rc;
778
779 ext4_filesystem_t *fs = inode_ref->fs;
780
781 /* Handle inode using extents */
782 if (ext4_superblock_has_feature_compatible(fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
783 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
784 // not reachable !!!
785 return ENOTSUP;
786 }
787
788 /* Handle simple case when we are dealing with direct reference */
789 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
790 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock, fblock);
791 inode_ref->dirty = true;
792 return EOK;
793 }
794
795 /* Determine the indirection level needed to get the desired block */
796 int level = -1;
797 for (int i = 1; i < 4; i++) {
798 if (iblock < fs->inode_block_limits[i]) {
799 level = i;
800 break;
801 }
802 }
803
804 if (level == -1) {
805 return EIO;
806 }
807
808 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
809
810 /* Compute offsets for the topmost level */
811 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
812 uint32_t current_block = ext4_inode_get_indirect_block(inode_ref->inode, level-1);
813 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
814
815 uint32_t new_block_addr;
816 block_t *block, *new_block;
817
818 if (current_block == 0) {
819 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
820 if (rc != EOK) {
821 return rc;
822 }
823
824 ext4_inode_set_indirect_block(inode_ref->inode, level - 1, new_block_addr);
825
826 inode_ref->dirty = true;
827
828 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
829 if (rc != EOK) {
830 ext4_balloc_free_block(inode_ref, new_block_addr);
831 return rc;
832 }
833
834 memset(new_block->data, 0, block_size);
835 new_block->dirty = true;
836
837 rc = block_put(new_block);
838 if (rc != EOK) {
839 return rc;
840 }
841
842 current_block = new_block_addr;
843 }
844
845 /* Navigate through other levels, until we find the block number
846 * or find null reference meaning we are dealing with sparse file
847 */
848 while (level > 0) {
849
850 rc = block_get(&block, fs->device, current_block, 0);
851 if (rc != EOK) {
852 return rc;
853 }
854
855 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
856
857 if ((level > 1) && (current_block == 0)) {
858 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
859 if (rc != EOK) {
860 block_put(block);
861 return rc;
862 }
863
864 rc = block_get(&new_block, fs->device, new_block_addr, BLOCK_FLAGS_NOREAD);
865 if (rc != EOK) {
866 block_put(block);
867 return rc;
868 }
869
870 memset(new_block->data, 0, block_size);
871 new_block->dirty = true;
872
873 rc = block_put(new_block);
874 if (rc != EOK) {
875 block_put(block);
876 return rc;
877 }
878
879 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(new_block_addr);
880 block->dirty = true;
881 current_block = new_block_addr;
882 }
883
884 if (level == 1) {
885 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(fblock);
886 block->dirty = true;
887 }
888
889 rc = block_put(block);
890 if (rc != EOK) {
891 return rc;
892 }
893
894 level -= 1;
895
896 /* If we are on the last level, break here as
897 * there is no next level to visit
898 */
899 if (level == 0) {
900 break;
901 }
902
903 /* Visit the next level */
904 block_offset_in_level %= fs->inode_blocks_per_level[level];
905 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
906 }
907
908 return EOK;
909}
910
911/** TODO comment
912 *
913 */
914int ext4_filesystem_release_inode_block(
915 ext4_inode_ref_t *inode_ref, uint32_t iblock)
916{
917 int rc;
918
919 uint32_t fblock;
920
921 ext4_filesystem_t *fs = inode_ref->fs;
922
923 // EXTENTS are handled otherwise
924 assert(! (ext4_superblock_has_feature_incompatible(fs->superblock,
925 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
926 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)));
927
928 ext4_inode_t *inode = inode_ref->inode;
929
930 /* Handle simple case when we are dealing with direct reference */
931 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
932 fblock = ext4_inode_get_direct_block(inode, iblock);
933 // Sparse file
934 if (fblock == 0) {
935 return EOK;
936 }
937
938 ext4_inode_set_direct_block(inode, iblock, 0);
939 return ext4_balloc_free_block(inode_ref, fblock);
940 }
941
942
943 /* Determine the indirection level needed to get the desired block */
944 int level = -1;
945 for (int i = 1; i < 4; i++) {
946 if (iblock < fs->inode_block_limits[i]) {
947 level = i;
948 break;
949 }
950 }
951
952 if (level == -1) {
953 return EIO;
954 }
955
956 /* Compute offsets for the topmost level */
957 aoff64_t block_offset_in_level = iblock - fs->inode_block_limits[level-1];
958 uint32_t current_block = ext4_inode_get_indirect_block(inode, level-1);
959 uint32_t offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
960
961 /* Navigate through other levels, until we find the block number
962 * or find null reference meaning we are dealing with sparse file
963 */
964 block_t *block;
965 while (level > 0) {
966 rc = block_get(&block, fs->device, current_block, 0);
967 if (rc != EOK) {
968 return rc;
969 }
970
971 current_block = uint32_t_le2host(((uint32_t*)block->data)[offset_in_block]);
972
973 // Set zero
974 if (level == 1) {
975 ((uint32_t*)block->data)[offset_in_block] = host2uint32_t_le(0);
976 block->dirty = true;
977 }
978
979 rc = block_put(block);
980 if (rc != EOK) {
981 return rc;
982 }
983
984 level -= 1;
985
986 /* If we are on the last level, break here as
987 * there is no next level to visit
988 */
989 if (level == 0) {
990 break;
991 }
992
993 /* Visit the next level */
994 block_offset_in_level %= fs->inode_blocks_per_level[level];
995 offset_in_block = block_offset_in_level / fs->inode_blocks_per_level[level-1];
996 }
997
998 fblock = current_block;
999
1000 if (fblock == 0) {
1001 return EOK;
1002 }
1003
1004 return ext4_balloc_free_block(inode_ref, fblock);
1005
1006}
1007
1008/** TODO comment
1009 *
1010 */
1011int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1012 uint32_t *fblock, uint32_t *iblock)
1013{
1014 int rc;
1015
1016 EXT4FS_DBG("");
1017
1018 // Handle extents separately
1019 if (ext4_superblock_has_feature_incompatible(
1020 inode_ref->fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1021 ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)) {
1022
1023 return ext4_extent_append_block(inode_ref, iblock, fblock);
1024
1025 }
1026
1027 ext4_superblock_t *sb = inode_ref->fs->superblock;
1028
1029 // Compute next block index and allocate data block
1030 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1031 uint32_t block_size = ext4_superblock_get_block_size(sb);
1032
1033 // TODO zarovnat inode size a ne assert!!!
1034 assert(inode_size % block_size == 0);
1035
1036 // Logical blocks are numbered from 0
1037 uint32_t new_block_idx = inode_size / block_size;
1038
1039 uint32_t phys_block;
1040 rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1041 if (rc != EOK) {
1042 return rc;
1043 }
1044
1045 rc = ext4_filesystem_set_inode_data_block_index(inode_ref, new_block_idx, phys_block);
1046 if (rc != EOK) {
1047 ext4_balloc_free_block(inode_ref, phys_block);
1048 return rc;
1049 }
1050
1051 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1052
1053 inode_ref->dirty = true;
1054
1055 *fblock = phys_block;
1056 *iblock = new_block_idx;
1057 return EOK;
1058}
1059
1060/** TODO comment
1061 *
1062 */
1063int ext4_filesystem_add_orphan(ext4_inode_ref_t *inode_ref)
1064{
1065 uint32_t next_orphan = ext4_superblock_get_last_orphan(
1066 inode_ref->fs->superblock);
1067 ext4_inode_set_deletion_time(inode_ref->inode, next_orphan);
1068 ext4_superblock_set_last_orphan(
1069 inode_ref->fs->superblock, inode_ref->index);
1070 inode_ref->dirty = true;
1071
1072 return EOK;
1073}
1074
1075/** TODO comment
1076 *
1077 */
1078int ext4_filesystem_delete_orphan(ext4_inode_ref_t *inode_ref)
1079{
1080 int rc;
1081
1082 uint32_t last_orphan = ext4_superblock_get_last_orphan(
1083 inode_ref->fs->superblock);
1084 assert(last_orphan > 0);
1085
1086 uint32_t next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1087
1088 if (last_orphan == inode_ref->index) {
1089 ext4_superblock_set_last_orphan(inode_ref->fs->superblock, next_orphan);
1090 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1091 inode_ref->dirty = true;
1092 return EOK;
1093 }
1094
1095 ext4_inode_ref_t *current;
1096 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, last_orphan, &current);
1097 if (rc != EOK) {
1098 return rc;
1099 }
1100 next_orphan = ext4_inode_get_deletion_time(current->inode);
1101
1102 bool found;
1103
1104 while (next_orphan != 0) {
1105 if (next_orphan == inode_ref->index) {
1106 next_orphan = ext4_inode_get_deletion_time(inode_ref->inode);
1107 ext4_inode_set_deletion_time(current->inode, next_orphan);
1108 current->dirty = true;
1109 found = true;
1110 break;
1111 }
1112
1113 ext4_filesystem_put_inode_ref(current);
1114
1115 rc = ext4_filesystem_get_inode_ref(inode_ref->fs, next_orphan, &current);
1116 if (rc != EOK) {
1117 return rc;
1118 }
1119 next_orphan = ext4_inode_get_deletion_time(current->inode);
1120
1121 }
1122
1123 ext4_inode_set_deletion_time(inode_ref->inode, 0);
1124
1125 rc = ext4_filesystem_put_inode_ref(current);
1126 if (rc != EOK) {
1127 return rc;
1128 }
1129
1130 if (!found) {
1131 return ENOENT;
1132 }
1133
1134 return EOK;
1135}
1136
1137/**
1138 * @}
1139 */
Note: See TracBrowser for help on using the repository browser.