source: mainline/uspace/lib/ext4/libext4_filesystem.c@ 447201e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 447201e was 447201e, checked in by Maurizio Lombardi <m.lombardi85@…>, 10 years ago

libext4: adds a function that, given a block group ref, calculates the number of blocks reserved to GDT+superblock backups.

  • Property mode set to 100644
File size: 39.1 KB
Line 
1/*
2 * Copyright (c) 2011 Martin Sucha
3 * Copyright (c) 2012 Frantisek Princ
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libext4
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 <ipc/vfs.h>
42#include <align.h>
43#include "libext4.h"
44
45/** Initialize filesystem and read all needed data.
46 *
47 * @param fs Filesystem instance to be initialized
48 * @param service_id Identifier if device with the filesystem
49 *
50 * @return Error code
51 *
52 */
53int ext4_filesystem_init(ext4_filesystem_t *fs, service_id_t service_id,
54 enum cache_mode cmode)
55{
56 ext4_superblock_t *temp_superblock = NULL;
57
58 fs->device = service_id;
59
60 /* Initialize block library (4096 is size of communication channel) */
61 int rc = block_init(EXCHANGE_SERIALIZE, fs->device, 4096);
62 if (rc != EOK)
63 goto err;
64
65 /* Read superblock from device to memory */
66 rc = ext4_superblock_read_direct(fs->device, &temp_superblock);
67 if (rc != EOK)
68 goto err_1;
69
70 /* Read block size from superblock and check */
71 uint32_t block_size = ext4_superblock_get_block_size(temp_superblock);
72 if (block_size > EXT4_MAX_BLOCK_SIZE) {
73 rc = ENOTSUP;
74 goto err_1;
75 }
76
77 /* Initialize block caching by libblock */
78 rc = block_cache_init(service_id, block_size, 0, cmode);
79 if (rc != EOK)
80 goto err_1;
81
82 /* Compute limits for indirect block levels */
83 uint32_t block_ids_per_block = block_size / sizeof(uint32_t);
84 fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
85 fs->inode_blocks_per_level[0] = 1;
86 for (unsigned int i = 1; i < 4; i++) {
87 fs->inode_blocks_per_level[i] = fs->inode_blocks_per_level[i - 1] *
88 block_ids_per_block;
89 fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
90 fs->inode_blocks_per_level[i];
91 }
92
93 /* Return loaded superblock */
94 fs->superblock = temp_superblock;
95
96 uint16_t state = ext4_superblock_get_state(fs->superblock);
97
98 if (((state & EXT4_SUPERBLOCK_STATE_VALID_FS) !=
99 EXT4_SUPERBLOCK_STATE_VALID_FS) ||
100 ((state & EXT4_SUPERBLOCK_STATE_ERROR_FS) ==
101 EXT4_SUPERBLOCK_STATE_ERROR_FS)) {
102 rc = ENOTSUP;
103 goto err_2;
104 }
105
106 /* Mark system as mounted */
107 ext4_superblock_set_state(fs->superblock, EXT4_SUPERBLOCK_STATE_ERROR_FS);
108 rc = ext4_superblock_write_direct(fs->device, fs->superblock);
109 if (rc != EOK)
110 goto err_2;
111
112 uint16_t mnt_count = ext4_superblock_get_mount_count(fs->superblock);
113 ext4_superblock_set_mount_count(fs->superblock, mnt_count + 1);
114
115 return EOK;
116
117err_2:
118 block_cache_fini(fs->device);
119err_1:
120 block_fini(fs->device);
121err:
122 if (temp_superblock)
123 ext4_superblock_release(temp_superblock);
124 return rc;
125}
126
127/** Destroy filesystem instance (used by unmount operation).
128 *
129 * @param fs Filesystem to be destroyed
130 *
131 * @return Error code
132 *
133 */
134int ext4_filesystem_fini(ext4_filesystem_t *fs)
135{
136 /* Write the superblock to the device */
137 ext4_superblock_set_state(fs->superblock, EXT4_SUPERBLOCK_STATE_VALID_FS);
138 int rc = ext4_superblock_write_direct(fs->device, fs->superblock);
139
140 /* Release memory space for superblock */
141 free(fs->superblock);
142
143 /* Finish work with block library */
144 block_cache_fini(fs->device);
145 block_fini(fs->device);
146
147 return rc;
148}
149
150/** Check sanity of the filesystem.
151 *
152 * Main is the check of the superblock structure.
153 *
154 * @param fs Filesystem to be checked
155 *
156 * @return Error code
157 *
158 */
159int ext4_filesystem_check_sanity(ext4_filesystem_t *fs)
160{
161 /* Check superblock */
162 return ext4_superblock_check_sanity(fs->superblock);
163}
164
165/** Check filesystem's features, if supported by this driver
166 *
167 * Function can return EOK and set read_only flag. It mean's that
168 * there are some not-supported features, that can cause problems
169 * during some write operations.
170 *
171 * @param fs Filesystem to be checked
172 * @param read_only Flag if filesystem should be mounted only for reading
173 *
174 * @return Error code
175 *
176 */
177int ext4_filesystem_check_features(ext4_filesystem_t *fs, bool *read_only)
178{
179 /* Feature flags are present only in higher revisions */
180 if (ext4_superblock_get_rev_level(fs->superblock) == 0) {
181 *read_only = false;
182 return EOK;
183 }
184
185 /*
186 * Check incompatible features - if filesystem has some,
187 * volume can't be mounted
188 */
189 uint32_t incompatible_features;
190 incompatible_features =
191 ext4_superblock_get_features_incompatible(fs->superblock);
192 incompatible_features &= ~EXT4_FEATURE_INCOMPAT_SUPP;
193 if (incompatible_features > 0)
194 return ENOTSUP;
195
196 /*
197 * Check read-only features, if filesystem has some,
198 * volume can be mount only in read-only mode
199 */
200 uint32_t compatible_read_only;
201 compatible_read_only =
202 ext4_superblock_get_features_read_only(fs->superblock);
203 compatible_read_only &= ~EXT4_FEATURE_RO_COMPAT_SUPP;
204 if (compatible_read_only > 0) {
205 *read_only = true;
206 return EOK;
207 }
208
209 return EOK;
210}
211
212
213/** Convert block address to relative index in block group.
214 *
215 * @param sb Superblock pointer
216 * @param block_addr Block number to convert
217 *
218 * @return Relative number of block
219 *
220 */
221uint32_t ext4_filesystem_blockaddr2_index_in_group(ext4_superblock_t *sb,
222 uint32_t block_addr)
223{
224 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb);
225 uint32_t first_block = ext4_superblock_get_first_data_block(sb);
226
227 /* First block == 0 or 1 */
228 if (first_block == 0)
229 return block_addr % blocks_per_group;
230 else
231 return (block_addr - 1) % blocks_per_group;
232}
233
234
235/** Convert relative block address in group to absolute address.
236 *
237 * @param sb Superblock pointer
238 *
239 * @return Absolute block address
240 *
241 */
242uint32_t ext4_filesystem_index_in_group2blockaddr(ext4_superblock_t *sb,
243 uint32_t index, uint32_t bgid)
244{
245 uint32_t blocks_per_group = ext4_superblock_get_blocks_per_group(sb);
246
247 if (ext4_superblock_get_first_data_block(sb) == 0)
248 return bgid * blocks_per_group + index;
249 else
250 return bgid * blocks_per_group + index + 1;
251}
252
253/** Initialize block bitmap in block group.
254 *
255 * @param bg_ref Reference to block group
256 *
257 * @return Error code
258 *
259 */
260static int ext4_filesystem_init_block_bitmap(ext4_block_group_ref_t *bg_ref)
261{
262 /* Load bitmap */
263 uint32_t bitmap_block_addr = ext4_block_group_get_block_bitmap(
264 bg_ref->block_group, bg_ref->fs->superblock);
265
266 block_t *bitmap_block;
267 int rc = block_get(&bitmap_block, bg_ref->fs->device,
268 bitmap_block_addr, BLOCK_FLAGS_NOREAD);
269 if (rc != EOK)
270 return rc;
271
272 uint8_t *bitmap = bitmap_block->data;
273
274 /* Initialize all bitmap bits to zero */
275 uint32_t block_size = ext4_superblock_get_block_size(bg_ref->fs->superblock);
276 memset(bitmap, 0, block_size);
277
278 /* Determine first block and first data block in group */
279 uint32_t first_idx = 0;
280
281 uint32_t first_data = ext4_balloc_get_first_data_block_in_group(
282 bg_ref->fs->superblock, bg_ref);
283 uint32_t first_data_idx = ext4_filesystem_blockaddr2_index_in_group(
284 bg_ref->fs->superblock, first_data);
285
286 /* Set bits from to first block to first data block - 1 to one (allocated) */
287 for (uint32_t block = first_idx; block < first_data_idx; ++block)
288 ext4_bitmap_set_bit(bitmap, block);
289
290 bitmap_block->dirty = true;
291
292 /* Save bitmap */
293 return block_put(bitmap_block);
294}
295
296/** Initialize i-node bitmap in block group.
297 *
298 * @param bg_ref Reference to block group
299 *
300 * @return Error code
301 *
302 */
303static int ext4_filesystem_init_inode_bitmap(ext4_block_group_ref_t *bg_ref)
304{
305 /* Load bitmap */
306 uint32_t bitmap_block_addr = ext4_block_group_get_inode_bitmap(
307 bg_ref->block_group, bg_ref->fs->superblock);
308 block_t *bitmap_block;
309
310 int rc = block_get(&bitmap_block, bg_ref->fs->device,
311 bitmap_block_addr, BLOCK_FLAGS_NOREAD);
312 if (rc != EOK)
313 return rc;
314
315 uint8_t *bitmap = bitmap_block->data;
316
317 /* Initialize all bitmap bits to zero */
318 uint32_t block_size = ext4_superblock_get_block_size(bg_ref->fs->superblock);
319 uint32_t inodes_per_group =
320 ext4_superblock_get_inodes_per_group(bg_ref->fs->superblock);
321 memset(bitmap, 0, (inodes_per_group + 7) / 8);
322
323 uint32_t start_bit = inodes_per_group;
324 uint32_t end_bit = block_size * 8;
325
326 uint32_t i;
327 for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
328 ext4_bitmap_set_bit(bitmap, i);
329
330 if (i < end_bit)
331 memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
332
333 bitmap_block->dirty = true;
334
335 /* Save bitmap */
336 return block_put(bitmap_block);
337}
338
339/** Initialize i-node table in block group.
340 *
341 * @param bg_ref Reference to block group
342 *
343 * @return Error code
344 *
345 */
346static int ext4_filesystem_init_inode_table(ext4_block_group_ref_t *bg_ref)
347{
348 ext4_superblock_t *sb = bg_ref->fs->superblock;
349
350 uint32_t inode_size = ext4_superblock_get_inode_size(sb);
351 uint32_t block_size = ext4_superblock_get_block_size(sb);
352 uint32_t inodes_per_block = block_size / inode_size;
353
354 uint32_t inodes_in_group =
355 ext4_superblock_get_inodes_in_group(sb, bg_ref->index);
356
357 uint32_t table_blocks = inodes_in_group / inodes_per_block;
358
359 if (inodes_in_group % inodes_per_block)
360 table_blocks++;
361
362 /* Compute initialization bounds */
363 uint32_t first_block = ext4_block_group_get_inode_table_first_block(
364 bg_ref->block_group, sb);
365
366 uint32_t last_block = first_block + table_blocks - 1;
367
368 /* Initialization of all itable blocks */
369 for (uint32_t fblock = first_block; fblock <= last_block; ++fblock) {
370 block_t *block;
371 int rc = block_get(&block, bg_ref->fs->device, fblock,
372 BLOCK_FLAGS_NOREAD);
373 if (rc != EOK)
374 return rc;
375
376 memset(block->data, 0, block_size);
377 block->dirty = true;
378
379 rc = block_put(block);
380 if (rc != EOK)
381 return rc;
382 }
383
384 return EOK;
385}
386
387/** Get reference to block group specified by index.
388 *
389 * @param fs Filesystem to find block group on
390 * @param bgid Index of block group to load
391 * @param ref Output pointer for reference
392 *
393 * @return Error code
394 *
395 */
396int ext4_filesystem_get_block_group_ref(ext4_filesystem_t *fs, uint32_t bgid,
397 ext4_block_group_ref_t **ref)
398{
399 /* Allocate memory for new structure */
400 ext4_block_group_ref_t *newref =
401 malloc(sizeof(ext4_block_group_ref_t));
402 if (newref == NULL)
403 return ENOMEM;
404
405 /* Compute number of descriptors, that fits in one data block */
406 uint32_t descriptors_per_block =
407 ext4_superblock_get_block_size(fs->superblock) /
408 ext4_superblock_get_desc_size(fs->superblock);
409
410 /* Block group descriptor table starts at the next block after superblock */
411 aoff64_t block_id =
412 ext4_superblock_get_first_data_block(fs->superblock) + 1;
413
414 /* Find the block containing the descriptor we are looking for */
415 block_id += bgid / descriptors_per_block;
416 uint32_t offset = (bgid % descriptors_per_block) *
417 ext4_superblock_get_desc_size(fs->superblock);
418
419 /* Load block with descriptors */
420 int rc = block_get(&newref->block, fs->device, block_id, 0);
421 if (rc != EOK) {
422 free(newref);
423 return rc;
424 }
425
426 /* Inititialize in-memory representation */
427 newref->block_group = newref->block->data + offset;
428 newref->fs = fs;
429 newref->index = bgid;
430 newref->dirty = false;
431
432 *ref = newref;
433
434 if (ext4_block_group_has_flag(newref->block_group,
435 EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
436 rc = ext4_filesystem_init_block_bitmap(newref);
437 if (rc != EOK) {
438 block_put(newref->block);
439 free(newref);
440 return rc;
441 }
442
443 ext4_block_group_clear_flag(newref->block_group,
444 EXT4_BLOCK_GROUP_BLOCK_UNINIT);
445
446 newref->dirty = true;
447 }
448
449 if (ext4_block_group_has_flag(newref->block_group,
450 EXT4_BLOCK_GROUP_INODE_UNINIT)) {
451 rc = ext4_filesystem_init_inode_bitmap(newref);
452 if (rc != EOK) {
453 block_put(newref->block);
454 free(newref);
455 return rc;
456 }
457
458 ext4_block_group_clear_flag(newref->block_group,
459 EXT4_BLOCK_GROUP_INODE_UNINIT);
460
461 if (!ext4_block_group_has_flag(newref->block_group,
462 EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
463 rc = ext4_filesystem_init_inode_table(newref);
464 if (rc != EOK)
465 return rc;
466
467 ext4_block_group_set_flag(newref->block_group,
468 EXT4_BLOCK_GROUP_ITABLE_ZEROED);
469 }
470
471 newref->dirty = true;
472 }
473
474 return EOK;
475}
476
477/** Compute checksum of block group descriptor.
478 *
479 * @param sb Superblock
480 * @param bgid Index of block group in the filesystem
481 * @param bg Block group to compute checksum for
482 *
483 * @return Checksum value
484 *
485 */
486static uint16_t ext4_filesystem_bg_checksum(ext4_superblock_t *sb, uint32_t bgid,
487 ext4_block_group_t *bg)
488{
489 /* If checksum not supported, 0 will be returned */
490 uint16_t crc = 0;
491
492 /* Compute the checksum only if the filesystem supports it */
493 if (ext4_superblock_has_feature_read_only(sb,
494 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
495 void *base = bg;
496 void *checksum = &bg->checksum;
497
498 uint32_t offset = (uint32_t) (checksum - base);
499
500 /* Convert block group index to little endian */
501 uint32_t le_group = host2uint32_t_le(bgid);
502
503 /* Initialization */
504 crc = crc16(~0, sb->uuid, sizeof(sb->uuid));
505
506 /* Include index of block group */
507 crc = crc16(crc, (uint8_t *) &le_group, sizeof(le_group));
508
509 /* Compute crc from the first part (stop before checksum field) */
510 crc = crc16(crc, (uint8_t *) bg, offset);
511
512 /* Skip checksum */
513 offset += sizeof(bg->checksum);
514
515 /* Checksum of the rest of block group descriptor */
516 if ((ext4_superblock_has_feature_incompatible(sb,
517 EXT4_FEATURE_INCOMPAT_64BIT)) &&
518 (offset < ext4_superblock_get_desc_size(sb)))
519 crc = crc16(crc, ((uint8_t *) bg) + offset,
520 ext4_superblock_get_desc_size(sb) - offset);
521 }
522
523 return crc;
524}
525
526/* Check if n is a power of p */
527static bool is_power_of(uint32_t n, unsigned p)
528{
529 if (p == 1 && n != p)
530 return false;
531
532 while (n != p) {
533 if (n < p)
534 return false;
535 else if ((n % p) != 0)
536 return false;
537
538 n /= p;
539 }
540
541 return true;
542}
543
544/** Get the number of blocks used by superblock + gdt + reserved gdt backups
545 *
546 * @param bg Pointer to block group
547 *
548 * @return Number of blocks
549 */
550uint32_t ext4_block_group_get_backup_blocks(ext4_block_group_ref_t *bg)
551{
552 uint32_t const idx = bg->index;
553 uint32_t r = 0;
554 bool has_backups = false;
555 ext4_superblock_t *sb = bg->fs->superblock;
556
557 /* First step: determine if the block group contains the backups */
558
559 if (idx <= 1)
560 has_backups = true;
561 else {
562 if (ext4_superblock_has_feature_compatible(sb,
563 EXT4_FEATURE_COMPAT_SPARSE_SUPER2)) {
564 uint32_t g1, g2;
565
566 ext4_superblock_get_backup_groups_sparse2(sb,
567 &g1, &g2);
568
569 if (idx == g1 || idx == g2)
570 has_backups = true;
571 } else if (!ext4_superblock_has_feature_read_only(sb,
572 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
573 /* Very old fs were all block groups have
574 * superblock and block descriptors backups.
575 */
576 has_backups = true;
577 } else {
578 if ((idx & 1) && (is_power_of(idx, 3) ||
579 is_power_of(idx, 5) || is_power_of(idx, 7)))
580 has_backups = true;
581 }
582 }
583
584 if (has_backups) {
585 uint32_t bg_count;
586 uint32_t bg_desc_sz;
587 uint32_t gdt_table; /* Size of the GDT in blocks */
588 uint32_t block_size = ext4_superblock_get_block_size(sb);
589
590 /* Now we know that this block group has backups,
591 * we have to compute how many blocks are reserved
592 * for them
593 */
594
595 if (idx == 0 && block_size == 1024) {
596 /* Special case for first group were the boot block
597 * resides
598 */
599 r++;
600 }
601
602 /* This accounts for the superblock */
603 r++;
604
605 /* Add the number of blocks used for the GDT */
606 bg_count = ext4_superblock_get_block_group_count(sb);
607 bg_desc_sz = ext4_superblock_get_desc_size(sb);
608 gdt_table = ROUND_UP(bg_count * bg_desc_sz, block_size) /
609 block_size;
610
611 r += gdt_table;
612
613 /* And now the number of reserved GDT blocks */
614 r += ext4_superblock_get_reserved_gdt_blocks(sb);
615 }
616
617 return r;
618}
619
620/** Put reference to block group.
621 *
622 * @oaram ref Pointer for reference to be put back
623 *
624 * @return Error code
625 *
626 */
627int ext4_filesystem_put_block_group_ref(ext4_block_group_ref_t *ref)
628{
629 /* Check if reference modified */
630 if (ref->dirty) {
631 /* Compute new checksum of block group */
632 uint16_t checksum =
633 ext4_filesystem_bg_checksum(ref->fs->superblock, ref->index,
634 ref->block_group);
635 ext4_block_group_set_checksum(ref->block_group, checksum);
636
637 /* Mark block dirty for writing changes to physical device */
638 ref->block->dirty = true;
639 }
640
641 /* Put back block, that contains block group descriptor */
642 int rc = block_put(ref->block);
643 free(ref);
644
645 return rc;
646}
647
648/** Get reference to i-node specified by index.
649 *
650 * @param fs Filesystem to find i-node on
651 * @param index Index of i-node to load
652 * @oaram ref Output pointer for reference
653 *
654 * @return Error code
655 *
656 */
657int ext4_filesystem_get_inode_ref(ext4_filesystem_t *fs, uint32_t index,
658 ext4_inode_ref_t **ref)
659{
660 /* Allocate memory for new structure */
661 ext4_inode_ref_t *newref =
662 malloc(sizeof(ext4_inode_ref_t));
663 if (newref == NULL)
664 return ENOMEM;
665
666 /* Compute number of i-nodes, that fits in one data block */
667 uint32_t inodes_per_group =
668 ext4_superblock_get_inodes_per_group(fs->superblock);
669
670 /*
671 * Inode numbers are 1-based, but it is simpler to work with 0-based
672 * when computing indices
673 */
674 index -= 1;
675 uint32_t block_group = index / inodes_per_group;
676 uint32_t offset_in_group = index % inodes_per_group;
677
678 /* Load block group, where i-node is located */
679 ext4_block_group_ref_t *bg_ref;
680 int rc = ext4_filesystem_get_block_group_ref(fs, block_group, &bg_ref);
681 if (rc != EOK) {
682 free(newref);
683 return rc;
684 }
685
686 /* Load block address, where i-node table is located */
687 uint32_t inode_table_start =
688 ext4_block_group_get_inode_table_first_block(bg_ref->block_group,
689 fs->superblock);
690
691 /* Put back block group reference (not needed more) */
692 rc = ext4_filesystem_put_block_group_ref(bg_ref);
693 if (rc != EOK) {
694 free(newref);
695 return rc;
696 }
697
698 /* Compute position of i-node in the block group */
699 uint16_t inode_size = ext4_superblock_get_inode_size(fs->superblock);
700 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
701 uint32_t byte_offset_in_group = offset_in_group * inode_size;
702
703 /* Compute block address */
704 aoff64_t block_id = inode_table_start + (byte_offset_in_group / block_size);
705 rc = block_get(&newref->block, fs->device, block_id, 0);
706 if (rc != EOK) {
707 free(newref);
708 return rc;
709 }
710
711 /* Compute position of i-node in the data block */
712 uint32_t offset_in_block = byte_offset_in_group % block_size;
713 newref->inode = newref->block->data + offset_in_block;
714
715 /* We need to store the original value of index in the reference */
716 newref->index = index + 1;
717 newref->fs = fs;
718 newref->dirty = false;
719
720 *ref = newref;
721
722 return EOK;
723}
724
725/** Put reference to i-node.
726 *
727 * @param ref Pointer for reference to be put back
728 *
729 * @return Error code
730 *
731 */
732int ext4_filesystem_put_inode_ref(ext4_inode_ref_t *ref)
733{
734 /* Check if reference modified */
735 if (ref->dirty) {
736 /* Mark block dirty for writing changes to physical device */
737 ref->block->dirty = true;
738 }
739
740 /* Put back block, that contains i-node */
741 int rc = block_put(ref->block);
742 free(ref);
743
744 return rc;
745}
746
747/** Allocate new i-node in the filesystem.
748 *
749 * @param fs Filesystem to allocated i-node on
750 * @param inode_ref Output pointer to return reference to allocated i-node
751 * @param flags Flags to be set for newly created i-node
752 *
753 * @return Error code
754 *
755 */
756int ext4_filesystem_alloc_inode(ext4_filesystem_t *fs,
757 ext4_inode_ref_t **inode_ref, int flags)
758{
759 /* Check if newly allocated i-node will be a directory */
760 bool is_dir = false;
761 if (flags & L_DIRECTORY)
762 is_dir = true;
763
764 /* Allocate inode by allocation algorithm */
765 uint32_t index;
766 int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
767 if (rc != EOK)
768 return rc;
769
770 /* Load i-node from on-disk i-node table */
771 rc = ext4_filesystem_get_inode_ref(fs, index, inode_ref);
772 if (rc != EOK) {
773 ext4_ialloc_free_inode(fs, index, is_dir);
774 return rc;
775 }
776
777 /* Initialize i-node */
778 ext4_inode_t *inode = (*inode_ref)->inode;
779
780 uint16_t mode;
781 if (is_dir) {
782 /*
783 * Default directory permissions to be compatible with other systems
784 * 0777 (octal) == rwxrwxrwx
785 */
786
787 mode = 0777;
788 mode |= EXT4_INODE_MODE_DIRECTORY;
789 ext4_inode_set_mode(fs->superblock, inode, mode);
790 ext4_inode_set_links_count(inode, 1); /* '.' entry */
791 } else {
792 /*
793 * Default file permissions to be compatible with other systems
794 * 0666 (octal) == rw-rw-rw-
795 */
796
797 mode = 0666;
798 mode |= EXT4_INODE_MODE_FILE;
799 ext4_inode_set_mode(fs->superblock, inode, mode);
800 ext4_inode_set_links_count(inode, 0);
801 }
802
803 ext4_inode_set_uid(inode, 0);
804 ext4_inode_set_gid(inode, 0);
805 ext4_inode_set_size(inode, 0);
806 ext4_inode_set_access_time(inode, 0);
807 ext4_inode_set_change_inode_time(inode, 0);
808 ext4_inode_set_modification_time(inode, 0);
809 ext4_inode_set_deletion_time(inode, 0);
810 ext4_inode_set_blocks_count(fs->superblock, inode, 0);
811 ext4_inode_set_flags(inode, 0);
812 ext4_inode_set_generation(inode, 0);
813
814 /* Reset blocks array */
815 for (uint32_t i = 0; i < EXT4_INODE_BLOCKS; i++)
816 inode->blocks[i] = 0;
817
818 /* Initialize extents if needed */
819 if (ext4_superblock_has_feature_incompatible(
820 fs->superblock, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
821 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
822
823 /* Initialize extent root header */
824 ext4_extent_header_t *header = ext4_inode_get_extent_header(inode);
825 ext4_extent_header_set_depth(header, 0);
826 ext4_extent_header_set_entries_count(header, 0);
827 ext4_extent_header_set_generation(header, 0);
828 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
829
830 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
831 sizeof(ext4_extent_header_t)) / sizeof(ext4_extent_t);
832
833 ext4_extent_header_set_max_entries_count(header, max_entries);
834 }
835
836 (*inode_ref)->dirty = true;
837
838 return EOK;
839}
840
841/** Release i-node and mark it as free.
842 *
843 * @param inode_ref I-node to be released
844 *
845 * @return Error code
846 *
847 */
848int ext4_filesystem_free_inode(ext4_inode_ref_t *inode_ref)
849{
850 ext4_filesystem_t *fs = inode_ref->fs;
851
852 /* For extents must be data block destroyed by other way */
853 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
854 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
855 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
856 /* Data structures are released during truncate operation... */
857 goto finish;
858 }
859
860 /* Release all indirect (no data) blocks */
861
862 /* 1) Single indirect */
863 uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
864 if (fblock != 0) {
865 int rc = ext4_balloc_free_block(inode_ref, fblock);
866 if (rc != EOK)
867 return rc;
868
869 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
870 }
871
872 block_t *block;
873 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
874 uint32_t count = block_size / sizeof(uint32_t);
875
876 /* 2) Double indirect */
877 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
878 if (fblock != 0) {
879 int rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
880 if (rc != EOK)
881 return rc;
882
883 uint32_t ind_block;
884 for (uint32_t offset = 0; offset < count; ++offset) {
885 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
886
887 if (ind_block != 0) {
888 rc = ext4_balloc_free_block(inode_ref, ind_block);
889 if (rc != EOK) {
890 block_put(block);
891 return rc;
892 }
893 }
894 }
895
896 rc = block_put(block);
897 if (rc != EOK)
898 return rc;
899
900 rc = ext4_balloc_free_block(inode_ref, fblock);
901 if (rc != EOK)
902 return rc;
903
904 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
905 }
906
907 /* 3) Tripple indirect */
908 block_t *subblock;
909 fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
910 if (fblock != 0) {
911 int rc = block_get(&block, fs->device, fblock, BLOCK_FLAGS_NONE);
912 if (rc != EOK)
913 return rc;
914
915 uint32_t ind_block;
916 for (uint32_t offset = 0; offset < count; ++offset) {
917 ind_block = uint32_t_le2host(((uint32_t *) block->data)[offset]);
918
919 if (ind_block != 0) {
920 rc = block_get(&subblock, fs->device, ind_block,
921 BLOCK_FLAGS_NONE);
922 if (rc != EOK) {
923 block_put(block);
924 return rc;
925 }
926
927 uint32_t ind_subblock;
928 for (uint32_t suboffset = 0; suboffset < count;
929 ++suboffset) {
930 ind_subblock = uint32_t_le2host(((uint32_t *)
931 subblock->data)[suboffset]);
932
933 if (ind_subblock != 0) {
934 rc = ext4_balloc_free_block(inode_ref, ind_subblock);
935 if (rc != EOK) {
936 block_put(subblock);
937 block_put(block);
938 return rc;
939 }
940 }
941 }
942
943 rc = block_put(subblock);
944 if (rc != EOK) {
945 block_put(block);
946 return rc;
947 }
948 }
949
950 rc = ext4_balloc_free_block(inode_ref, ind_block);
951 if (rc != EOK) {
952 block_put(block);
953 return rc;
954 }
955 }
956
957 rc = block_put(block);
958 if (rc != EOK)
959 return rc;
960
961 rc = ext4_balloc_free_block(inode_ref, fblock);
962 if (rc != EOK)
963 return rc;
964
965 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
966 }
967
968finish:
969 /* Mark inode dirty for writing to the physical device */
970 inode_ref->dirty = true;
971
972 /* Free block with extended attributes if present */
973 uint32_t xattr_block = ext4_inode_get_file_acl(
974 inode_ref->inode, fs->superblock);
975 if (xattr_block) {
976 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
977 if (rc != EOK)
978 return rc;
979
980 ext4_inode_set_file_acl(inode_ref->inode, fs->superblock, 0);
981 }
982
983 /* Free inode by allocator */
984 int rc;
985 if (ext4_inode_is_type(fs->superblock, inode_ref->inode,
986 EXT4_INODE_MODE_DIRECTORY))
987 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
988 else
989 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
990
991 return rc;
992}
993
994/** Truncate i-node data blocks.
995 *
996 * @param inode_ref I-node to be truncated
997 * @param new_size New size of inode (must be < current size)
998 *
999 * @return Error code
1000 *
1001 */
1002int ext4_filesystem_truncate_inode(ext4_inode_ref_t *inode_ref,
1003 aoff64_t new_size)
1004{
1005 ext4_superblock_t *sb = inode_ref->fs->superblock;
1006
1007 /* Check flags, if i-node can be truncated */
1008 if (!ext4_inode_can_truncate(sb, inode_ref->inode))
1009 return EINVAL;
1010
1011 /* If sizes are equal, nothing has to be done. */
1012 aoff64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
1013 if (old_size == new_size)
1014 return EOK;
1015
1016 /* It's not suppported to make the larger file by truncate operation */
1017 if (old_size < new_size)
1018 return EINVAL;
1019
1020 /* Compute how many blocks will be released */
1021 aoff64_t size_diff = old_size - new_size;
1022 uint32_t block_size = ext4_superblock_get_block_size(sb);
1023 uint32_t diff_blocks_count = size_diff / block_size;
1024 if (size_diff % block_size != 0)
1025 diff_blocks_count++;
1026
1027 uint32_t old_blocks_count = old_size / block_size;
1028 if (old_size % block_size != 0)
1029 old_blocks_count++;
1030
1031 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1032 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1033 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1034 /* Extents require special operation */
1035 int rc = ext4_extent_release_blocks_from(inode_ref,
1036 old_blocks_count - diff_blocks_count);
1037 if (rc != EOK)
1038 return rc;
1039 } else {
1040 /* Release data blocks from the end of file */
1041
1042 /* Starting from 1 because of logical blocks are numbered from 0 */
1043 for (uint32_t i = 1; i <= diff_blocks_count; ++i) {
1044 int rc = ext4_filesystem_release_inode_block(inode_ref,
1045 old_blocks_count - i);
1046 if (rc != EOK)
1047 return rc;
1048 }
1049 }
1050
1051 /* Update i-node */
1052 ext4_inode_set_size(inode_ref->inode, new_size);
1053 inode_ref->dirty = true;
1054
1055 return EOK;
1056}
1057
1058/** Get physical block address by logical index of the block.
1059 *
1060 * @param inode_ref I-node to read block address from
1061 * @param iblock Logical index of block
1062 * @param fblock Output pointer for return physical block address
1063 *
1064 * @return Error code
1065 *
1066 */
1067int ext4_filesystem_get_inode_data_block_index(ext4_inode_ref_t *inode_ref,
1068 aoff64_t iblock, uint32_t *fblock)
1069{
1070 ext4_filesystem_t *fs = inode_ref->fs;
1071
1072 /* For empty file is situation simple */
1073 if (ext4_inode_get_size(fs->superblock, inode_ref->inode) == 0) {
1074 *fblock = 0;
1075 return EOK;
1076 }
1077
1078 uint32_t current_block;
1079
1080 /* Handle i-node using extents */
1081 if ((ext4_superblock_has_feature_incompatible(fs->superblock,
1082 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1083 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1084 int rc = ext4_extent_find_block(inode_ref, iblock, &current_block);
1085 if (rc != EOK)
1086 return rc;
1087
1088 *fblock = current_block;
1089 return EOK;
1090 }
1091
1092 ext4_inode_t *inode = inode_ref->inode;
1093
1094 /* Direct block are read directly from array in i-node structure */
1095 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1096 current_block = ext4_inode_get_direct_block(inode, (uint32_t) iblock);
1097 *fblock = current_block;
1098 return EOK;
1099 }
1100
1101 /* Determine indirection level of the target block */
1102 unsigned int level = 0;
1103 for (unsigned int i = 1; i < 4; i++) {
1104 if (iblock < fs->inode_block_limits[i]) {
1105 level = i;
1106 break;
1107 }
1108 }
1109
1110 if (level == 0)
1111 return EIO;
1112
1113 /* Compute offsets for the topmost level */
1114 aoff64_t block_offset_in_level =
1115 iblock - fs->inode_block_limits[level - 1];
1116 current_block = ext4_inode_get_indirect_block(inode, level - 1);
1117 uint32_t offset_in_block =
1118 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1119
1120 /* Sparse file */
1121 if (current_block == 0) {
1122 *fblock = 0;
1123 return EOK;
1124 }
1125
1126 block_t *block;
1127
1128 /*
1129 * Navigate through other levels, until we find the block number
1130 * or find null reference meaning we are dealing with sparse file
1131 */
1132 while (level > 0) {
1133 /* Load indirect block */
1134 int rc = block_get(&block, fs->device, current_block, 0);
1135 if (rc != EOK)
1136 return rc;
1137
1138 /* Read block address from indirect block */
1139 current_block =
1140 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1141
1142 /* Put back indirect block untouched */
1143 rc = block_put(block);
1144 if (rc != EOK)
1145 return rc;
1146
1147 /* Check for sparse file */
1148 if (current_block == 0) {
1149 *fblock = 0;
1150 return EOK;
1151 }
1152
1153 /* Jump to the next level */
1154 level--;
1155
1156 /* Termination condition - we have address of data block loaded */
1157 if (level == 0)
1158 break;
1159
1160 /* Visit the next level */
1161 block_offset_in_level %= fs->inode_blocks_per_level[level];
1162 offset_in_block =
1163 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1164 }
1165
1166 *fblock = current_block;
1167
1168 return EOK;
1169}
1170
1171/** Set physical block address for the block logical address into the i-node.
1172 *
1173 * @param inode_ref I-node to set block address to
1174 * @param iblock Logical index of block
1175 * @param fblock Physical block address
1176 *
1177 * @return Error code
1178 *
1179 */
1180int ext4_filesystem_set_inode_data_block_index(ext4_inode_ref_t *inode_ref,
1181 aoff64_t iblock, uint32_t fblock)
1182{
1183 ext4_filesystem_t *fs = inode_ref->fs;
1184
1185 /* Handle inode using extents */
1186 if ((ext4_superblock_has_feature_compatible(fs->superblock,
1187 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1188 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1189 /* Not reachable */
1190 return ENOTSUP;
1191 }
1192
1193 /* Handle simple case when we are dealing with direct reference */
1194 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1195 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t) iblock, fblock);
1196 inode_ref->dirty = true;
1197
1198 return EOK;
1199 }
1200
1201 /* Determine the indirection level needed to get the desired block */
1202 unsigned int level = 0;
1203 for (unsigned int i = 1; i < 4; i++) {
1204 if (iblock < fs->inode_block_limits[i]) {
1205 level = i;
1206 break;
1207 }
1208 }
1209
1210 if (level == 0)
1211 return EIO;
1212
1213 uint32_t block_size = ext4_superblock_get_block_size(fs->superblock);
1214
1215 /* Compute offsets for the topmost level */
1216 aoff64_t block_offset_in_level =
1217 iblock - fs->inode_block_limits[level - 1];
1218 uint32_t current_block =
1219 ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1220 uint32_t offset_in_block =
1221 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1222
1223 uint32_t new_block_addr;
1224 block_t *block;
1225 block_t *new_block;
1226
1227 /* Is needed to allocate indirect block on the i-node level */
1228 if (current_block == 0) {
1229 /* Allocate new indirect block */
1230 int rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1231 if (rc != EOK)
1232 return rc;
1233
1234 /* Update i-node */
1235 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1236 new_block_addr);
1237 inode_ref->dirty = true;
1238
1239 /* Load newly allocated block */
1240 rc = block_get(&new_block, fs->device, new_block_addr,
1241 BLOCK_FLAGS_NOREAD);
1242 if (rc != EOK) {
1243 ext4_balloc_free_block(inode_ref, new_block_addr);
1244 return rc;
1245 }
1246
1247 /* Initialize new block */
1248 memset(new_block->data, 0, block_size);
1249 new_block->dirty = true;
1250
1251 /* Put back the allocated block */
1252 rc = block_put(new_block);
1253 if (rc != EOK)
1254 return rc;
1255
1256 current_block = new_block_addr;
1257 }
1258
1259 /*
1260 * Navigate through other levels, until we find the block number
1261 * or find null reference meaning we are dealing with sparse file
1262 */
1263 while (level > 0) {
1264 int rc = block_get(&block, fs->device, current_block, 0);
1265 if (rc != EOK)
1266 return rc;
1267
1268 current_block =
1269 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1270
1271 if ((level > 1) && (current_block == 0)) {
1272 /* Allocate new block */
1273 rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1274 if (rc != EOK) {
1275 block_put(block);
1276 return rc;
1277 }
1278
1279 /* Load newly allocated block */
1280 rc = block_get(&new_block, fs->device, new_block_addr,
1281 BLOCK_FLAGS_NOREAD);
1282 if (rc != EOK) {
1283 block_put(block);
1284 return rc;
1285 }
1286
1287 /* Initialize allocated block */
1288 memset(new_block->data, 0, block_size);
1289 new_block->dirty = true;
1290
1291 rc = block_put(new_block);
1292 if (rc != EOK) {
1293 block_put(block);
1294 return rc;
1295 }
1296
1297 /* Write block address to the parent */
1298 ((uint32_t *) block->data)[offset_in_block] =
1299 host2uint32_t_le(new_block_addr);
1300 block->dirty = true;
1301 current_block = new_block_addr;
1302 }
1303
1304 /* Will be finished, write the fblock address */
1305 if (level == 1) {
1306 ((uint32_t *) block->data)[offset_in_block] =
1307 host2uint32_t_le(fblock);
1308 block->dirty = true;
1309 }
1310
1311 rc = block_put(block);
1312 if (rc != EOK)
1313 return rc;
1314
1315 level--;
1316
1317 /*
1318 * If we are on the last level, break here as
1319 * there is no next level to visit
1320 */
1321 if (level == 0)
1322 break;
1323
1324 /* Visit the next level */
1325 block_offset_in_level %= fs->inode_blocks_per_level[level];
1326 offset_in_block =
1327 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1328 }
1329
1330 return EOK;
1331}
1332
1333/** Release data block from i-node
1334 *
1335 * @param inode_ref I-node to release block from
1336 * @param iblock Logical block to be released
1337 *
1338 * @return Error code
1339 *
1340 */
1341int ext4_filesystem_release_inode_block(ext4_inode_ref_t *inode_ref,
1342 uint32_t iblock)
1343{
1344 uint32_t fblock;
1345
1346 ext4_filesystem_t *fs = inode_ref->fs;
1347
1348 /* Extents are handled otherwise = there is not support in this function */
1349 assert(!(ext4_superblock_has_feature_incompatible(fs->superblock,
1350 EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1351 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1352
1353 ext4_inode_t *inode = inode_ref->inode;
1354
1355 /* Handle simple case when we are dealing with direct reference */
1356 if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1357 fblock = ext4_inode_get_direct_block(inode, iblock);
1358
1359 /* Sparse file */
1360 if (fblock == 0)
1361 return EOK;
1362
1363 ext4_inode_set_direct_block(inode, iblock, 0);
1364 return ext4_balloc_free_block(inode_ref, fblock);
1365 }
1366
1367 /* Determine the indirection level needed to get the desired block */
1368 unsigned int level = 0;
1369 for (unsigned int i = 1; i < 4; i++) {
1370 if (iblock < fs->inode_block_limits[i]) {
1371 level = i;
1372 break;
1373 }
1374 }
1375
1376 if (level == 0)
1377 return EIO;
1378
1379 /* Compute offsets for the topmost level */
1380 aoff64_t block_offset_in_level =
1381 iblock - fs->inode_block_limits[level - 1];
1382 uint32_t current_block =
1383 ext4_inode_get_indirect_block(inode, level - 1);
1384 uint32_t offset_in_block =
1385 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1386
1387 /*
1388 * Navigate through other levels, until we find the block number
1389 * or find null reference meaning we are dealing with sparse file
1390 */
1391 block_t *block;
1392 while (level > 0) {
1393
1394 /* Sparse check */
1395 if (current_block == 0)
1396 return EOK;
1397
1398 int rc = block_get(&block, fs->device, current_block, 0);
1399 if (rc != EOK)
1400 return rc;
1401
1402 current_block =
1403 uint32_t_le2host(((uint32_t *) block->data)[offset_in_block]);
1404
1405 /* Set zero if physical data block address found */
1406 if (level == 1) {
1407 ((uint32_t *) block->data)[offset_in_block] =
1408 host2uint32_t_le(0);
1409 block->dirty = true;
1410 }
1411
1412 rc = block_put(block);
1413 if (rc != EOK)
1414 return rc;
1415
1416 level--;
1417
1418 /*
1419 * If we are on the last level, break here as
1420 * there is no next level to visit
1421 */
1422 if (level == 0)
1423 break;
1424
1425 /* Visit the next level */
1426 block_offset_in_level %= fs->inode_blocks_per_level[level];
1427 offset_in_block =
1428 block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1429 }
1430
1431 fblock = current_block;
1432 if (fblock == 0)
1433 return EOK;
1434
1435 /* Physical block is not referenced, it can be released */
1436 return ext4_balloc_free_block(inode_ref, fblock);
1437}
1438
1439/** Append following logical block to the i-node.
1440 *
1441 * @param inode_ref I-node to append block to
1442 * @param fblock Output physical block address of newly allocated block
1443 * @param iblock Output logical number of newly allocated block
1444 *
1445 * @return Error code
1446 *
1447 */
1448int ext4_filesystem_append_inode_block(ext4_inode_ref_t *inode_ref,
1449 uint32_t *fblock, uint32_t *iblock)
1450{
1451 /* Handle extents separately */
1452 if ((ext4_superblock_has_feature_incompatible(inode_ref->fs->superblock,
1453 EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1454 (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS)))
1455 return ext4_extent_append_block(inode_ref, iblock, fblock, true);
1456
1457 ext4_superblock_t *sb = inode_ref->fs->superblock;
1458
1459 /* Compute next block index and allocate data block */
1460 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1461 uint32_t block_size = ext4_superblock_get_block_size(sb);
1462
1463 /* Align size i-node size */
1464 if ((inode_size % block_size) != 0)
1465 inode_size += block_size - (inode_size % block_size);
1466
1467 /* Logical blocks are numbered from 0 */
1468 uint32_t new_block_idx = inode_size / block_size;
1469
1470 /* Allocate new physical block */
1471 uint32_t phys_block;
1472 int rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1473 if (rc != EOK)
1474 return rc;
1475
1476 /* Add physical block address to the i-node */
1477 rc = ext4_filesystem_set_inode_data_block_index(inode_ref,
1478 new_block_idx, phys_block);
1479 if (rc != EOK) {
1480 ext4_balloc_free_block(inode_ref, phys_block);
1481 return rc;
1482 }
1483
1484 /* Update i-node */
1485 ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1486 inode_ref->dirty = true;
1487
1488 *fblock = phys_block;
1489 *iblock = new_block_idx;
1490
1491 return EOK;
1492}
1493
1494/**
1495 * @}
1496 */
Note: See TracBrowser for help on using the repository browser.