Revert "Symbolic link support: ext4_fsymlink proposed."
[lwext4.git] / lwext4 / ext4_fs.c
1 /*
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  *
4  *
5  * HelenOS:
6  * Copyright (c) 2012 Martin Sucha
7  * Copyright (c) 2012 Frantisek Princ
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * - Redistributions of source code must retain the above copyright
15  *   notice, this list of conditions and the following disclaimer.
16  * - Redistributions in binary form must reproduce the above copyright
17  *   notice, this list of conditions and the following disclaimer in the
18  *   documentation and/or other materials provided with the distribution.
19  * - The name of the author may not be used to endorse or promote products
20  *   derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 /** @addtogroup lwext4
34  * @{
35  */
36 /**
37  * @file  ext4_fs.c
38  * @brief More complex filesystem functions.
39  */
40
41 #include "ext4_config.h"
42 #include "ext4_types.h"
43 #include "ext4_fs.h"
44 #include "ext4_errno.h"
45 #include "ext4_blockdev.h"
46 #include "ext4_super.h"
47 #include "ext4_debug.h"
48 #include "ext4_block_group.h"
49 #include "ext4_balloc.h"
50 #include "ext4_bitmap.h"
51 #include "ext4_inode.h"
52 #include "ext4_ialloc.h"
53 #include "ext4_extent.h"
54
55 #include <string.h>
56
57 int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev)
58 {
59         int r, i;
60         uint16_t tmp;
61         uint32_t bsize;
62         bool read_only = false;
63
64         ext4_assert(fs && bdev);
65
66         fs->bdev = bdev;
67
68         r = ext4_sb_read(fs->bdev, &fs->sb);
69         if (r != EOK)
70                 return r;
71
72         if (!ext4_sb_check(&fs->sb))
73                 return ENOTSUP;
74
75         bsize = ext4_sb_get_block_size(&fs->sb);
76         if (bsize > EXT4_MAX_BLOCK_SIZE)
77                 return ENXIO;
78
79         r = ext4_fs_check_features(fs, &read_only);
80         if (r != EOK)
81                 return r;
82
83         if (read_only)
84                 return ENOTSUP;
85
86         /* Compute limits for indirect block levels */
87         uint32_t blocks_id = bsize / sizeof(uint32_t);
88
89         fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
90         fs->inode_blocks_per_level[0] = 1;
91
92         for (i = 1; i < 4; i++) {
93                 fs->inode_blocks_per_level[i] =
94                     fs->inode_blocks_per_level[i - 1] * blocks_id;
95                 fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
96                                             fs->inode_blocks_per_level[i];
97         }
98
99         /*Validate FS*/
100         tmp = ext4_get16(&fs->sb, state);
101         if (tmp & EXT4_SUPERBLOCK_STATE_ERROR_FS) {
102                 ext4_dprintf(EXT4_DEBUG_FS, "last umount error\n");
103         }
104
105         /* Mark system as mounted */
106         ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_ERROR_FS);
107         r = ext4_sb_write(fs->bdev, &fs->sb);
108         if (r != EOK)
109                 return r;
110
111         /*Update mount count*/
112         ext4_set16(&fs->sb, mount_count, ext4_get16(&fs->sb, mount_count) + 1);
113
114         return r;
115 }
116
117 int ext4_fs_fini(struct ext4_fs *fs)
118 {
119         ext4_assert(fs);
120
121         /*Set superblock state*/
122         ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_VALID_FS);
123
124         return ext4_sb_write(fs->bdev, &fs->sb);
125 }
126
127 static void ext4_fs_debug_features_incomp(uint32_t features_incompatible)
128 {
129
130         if (features_incompatible & EXT4_FEATURE_INCOMPAT_COMPRESSION) {
131                 ext4_dprintf(EXT4_DEBUG_FS, "compression\n");
132         }
133         if (features_incompatible & EXT4_FEATURE_INCOMPAT_FILETYPE) {
134                 ext4_dprintf(EXT4_DEBUG_FS, "filetype\n");
135         }
136         if (features_incompatible & EXT4_FEATURE_INCOMPAT_RECOVER) {
137                 ext4_dprintf(EXT4_DEBUG_FS, "recover\n");
138         }
139         if (features_incompatible & EXT4_FEATURE_INCOMPAT_JOURNAL_DEV) {
140                 ext4_dprintf(EXT4_DEBUG_FS, "journal_dev\n");
141         }
142         if (features_incompatible & EXT4_FEATURE_INCOMPAT_META_BG) {
143                 ext4_dprintf(EXT4_DEBUG_FS, "meta_bg\n");
144         }
145         if (features_incompatible & EXT4_FEATURE_INCOMPAT_EXTENTS) {
146                 ext4_dprintf(EXT4_DEBUG_FS, "extents\n");
147         }
148         if (features_incompatible & EXT4_FEATURE_INCOMPAT_64BIT) {
149                 ext4_dprintf(EXT4_DEBUG_FS, "64bit\n");
150         }
151         if (features_incompatible & EXT4_FEATURE_INCOMPAT_MMP) {
152                 ext4_dprintf(EXT4_DEBUG_FS, "mnp\n");
153         }
154         if (features_incompatible & EXT4_FEATURE_INCOMPAT_FLEX_BG) {
155                 ext4_dprintf(EXT4_DEBUG_FS, "flex_bg\n");
156         }
157         if (features_incompatible & EXT4_FEATURE_INCOMPAT_EA_INODE) {
158                 ext4_dprintf(EXT4_DEBUG_FS, "ea_inode\n");
159         }
160         if (features_incompatible & EXT4_FEATURE_INCOMPAT_DIRDATA) {
161                 ext4_dprintf(EXT4_DEBUG_FS, "dirdata\n");
162         }
163         if (features_incompatible & EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM) {
164                 ext4_dprintf(EXT4_DEBUG_FS, "meta_csum\n");
165         }
166         if (features_incompatible & EXT4_FEATURE_INCOMPAT_LARGEDIR) {
167                 ext4_dprintf(EXT4_DEBUG_FS, "largedir\n");
168         }
169         if (features_incompatible & EXT4_FEATURE_INCOMPAT_INLINE_DATA) {
170                 ext4_dprintf(EXT4_DEBUG_FS, "inline_data\n");
171         }
172 }
173 static void ext4_fs_debug_features_comp(uint32_t features_compatible)
174 {
175         if (features_compatible & EXT4_FEATURE_COMPAT_DIR_PREALLOC) {
176                 ext4_dprintf(EXT4_DEBUG_FS, " dir_prealloc\n");
177         }
178         if (features_compatible & EXT4_FEATURE_COMPAT_IMAGIC_INODES) {
179                 ext4_dprintf(EXT4_DEBUG_FS, "imagic_inodes\n");
180         }
181         if (features_compatible & EXT4_FEATURE_COMPAT_HAS_JOURNAL) {
182                 ext4_dprintf(EXT4_DEBUG_FS, "has_journal\n");
183         }
184         if (features_compatible & EXT4_FEATURE_COMPAT_EXT_ATTR) {
185                 ext4_dprintf(EXT4_DEBUG_FS, "ext_attr\n");
186         }
187         if (features_compatible & EXT4_FEATURE_COMPAT_RESIZE_INODE) {
188                 ext4_dprintf(EXT4_DEBUG_FS, "resize_inode\n");
189         }
190         if (features_compatible & EXT4_FEATURE_COMPAT_DIR_INDEX) {
191                 ext4_dprintf(EXT4_DEBUG_FS, "dir_index\n");
192         }
193 }
194
195 static void ext4_fs_debug_features_ro(uint32_t features_ro)
196 {
197         if (features_ro & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER) {
198                 ext4_dprintf(EXT4_DEBUG_FS, "sparse_super\n");
199         }
200         if (features_ro & EXT4_FEATURE_RO_COMPAT_LARGE_FILE) {
201                 ext4_dprintf(EXT4_DEBUG_FS, "large_file\n");
202         }
203         if (features_ro & EXT4_FEATURE_RO_COMPAT_BTREE_DIR) {
204                 ext4_dprintf(EXT4_DEBUG_FS, "btree_dir\n");
205         }
206         if (features_ro & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) {
207                 ext4_dprintf(EXT4_DEBUG_FS, "huge_file\n");
208         }
209         if (features_ro & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
210                 ext4_dprintf(EXT4_DEBUG_FS, "gtd_csum\n");
211         }
212         if (features_ro & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) {
213                 ext4_dprintf(EXT4_DEBUG_FS, "dir_nlink\n");
214         }
215         if (features_ro & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) {
216                 ext4_dprintf(EXT4_DEBUG_FS, "extra_isize\n");
217         }
218         if (features_ro & EXT4_FEATURE_RO_COMPAT_QUOTA) {
219                 ext4_dprintf(EXT4_DEBUG_FS, "quota\n");
220         }
221         if (features_ro & EXT4_FEATURE_RO_COMPAT_BIGALLOC) {
222                 ext4_dprintf(EXT4_DEBUG_FS, "bigalloc\n");
223         }
224         if (features_ro & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
225                 ext4_dprintf(EXT4_DEBUG_FS, "metadata_csum\n");
226         }
227 }
228
229 int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only)
230 {
231         ext4_assert(fs && read_only);
232         uint32_t v;
233         if (ext4_get32(&fs->sb, rev_level) == 0) {
234                 *read_only = false;
235                 return EOK;
236         }
237
238         ext4_dprintf(EXT4_DEBUG_FS, "\nSB features_incompatible:\n");
239         ext4_fs_debug_features_incomp(
240             ext4_get32(&fs->sb, features_incompatible));
241
242         ext4_dprintf(EXT4_DEBUG_FS, "\nSB features_compatible:\n");
243         ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible));
244
245         ext4_dprintf(EXT4_DEBUG_FS, "\nSB features_read_only:\n");
246         ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only));
247
248         /*Check features_incompatible*/
249         v = (ext4_get32(&fs->sb, features_incompatible) &
250              (~CONFIG_FEATURE_INCOMPAT_SUPP));
251         if (v) {
252                 ext4_dprintf(EXT4_DEBUG_FS, "SB features_incompatible: fail\n");
253                 ext4_fs_debug_features_incomp(v);
254                 return ENOTSUP;
255         }
256
257         /*Check features_read_only*/
258         v = (ext4_get32(&fs->sb, features_read_only) &
259              (~CONFIG_FEATURE_RO_COMPAT_SUPP));
260         if (v) {
261                 ext4_dprintf(
262                     EXT4_DEBUG_FS,
263                     "\nERROR sblock features_read_only . Unsupported:\n");
264                 ext4_fs_debug_features_incomp(v);
265
266                 *read_only = true;
267                 return EOK;
268         }
269         *read_only = false;
270
271         return EOK;
272 }
273
274 /**@brief Initialize block bitmap in block group.
275  * @param bg_ref Reference to block group
276  * @return Error code
277  */
278 static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref)
279 {
280         uint32_t i;
281         uint32_t bitmap_block_addr =
282             ext4_bg_get_block_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
283
284         struct ext4_block block_bitmap;
285         int rc =
286             ext4_block_get(bg_ref->fs->bdev, &block_bitmap, bitmap_block_addr);
287         if (rc != EOK)
288                 return rc;
289
290         memset(block_bitmap.data, 0, ext4_sb_get_block_size(&bg_ref->fs->sb));
291
292         /* Determine first block and first data block in group */
293         uint32_t first_idx = 0;
294
295         uint32_t first_data =
296             ext4_balloc_get_first_data_block_in_group(&bg_ref->fs->sb, bg_ref);
297         uint32_t first_data_idx =
298             ext4_fs_baddr2_index_in_group(&bg_ref->fs->sb, first_data);
299
300         /*Set bits from to first block to first data block - 1 to one
301          * (allocated)*/
302         /*TODO: Optimize it*/
303         for (i = first_idx; i < first_data_idx; ++i)
304                 ext4_bmap_bit_set(block_bitmap.data, i);
305
306         block_bitmap.dirty = true;
307
308         /* Save bitmap */
309         return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
310 }
311
312 /**@brief Initialize i-node bitmap in block group.
313  * @param bg_ref Reference to block group
314  * @return Error code
315  */
316 static int ext4_fs_init_inode_bitmap(struct ext4_block_group_ref *bg_ref)
317 {
318         /* Load bitmap */
319         uint32_t bitmap_block_addr =
320             ext4_bg_get_inode_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
321
322         struct ext4_block block_bitmap;
323         int rc =
324             ext4_block_get(bg_ref->fs->bdev, &block_bitmap, bitmap_block_addr);
325         if (rc != EOK)
326                 return rc;
327
328         /* Initialize all bitmap bits to zero */
329         uint32_t block_size = ext4_sb_get_block_size(&bg_ref->fs->sb);
330         uint32_t inodes_per_group =
331             ext4_get32(&bg_ref->fs->sb, inodes_per_group);
332
333         memset(block_bitmap.data, 0, (inodes_per_group + 7) / 8);
334
335         uint32_t start_bit = inodes_per_group;
336         uint32_t end_bit = block_size * 8;
337
338         uint32_t i;
339         for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
340                 ext4_bmap_bit_set(block_bitmap.data, i);
341
342         if (i < end_bit)
343                 memset(block_bitmap.data + (i >> 3), 0xff, (end_bit - i) >> 3);
344
345         block_bitmap.dirty = true;
346
347         /* Save bitmap */
348         return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
349 }
350
351 /**@brief Initialize i-node table in block group.
352  * @param bg_ref Reference to block group
353  * @return Error code
354  */
355 static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
356 {
357         struct ext4_sblock *sb = &bg_ref->fs->sb;
358
359         uint32_t inode_size = ext4_get32(sb, inode_size);
360         uint32_t block_size = ext4_sb_get_block_size(sb);
361         uint32_t inodes_per_block = block_size / inode_size;
362         uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index);
363         uint32_t table_blocks = inodes_in_group / inodes_per_block;
364         uint32_t fblock;
365
366         if (inodes_in_group % inodes_per_block)
367                 table_blocks++;
368
369         /* Compute initialization bounds */
370         uint32_t first_block =
371             ext4_bg_get_inode_table_first_block(bg_ref->block_group, sb);
372
373         uint32_t last_block = first_block + table_blocks - 1;
374
375         /* Initialization of all itable blocks */
376         for (fblock = first_block; fblock <= last_block; ++fblock) {
377
378                 struct ext4_block block;
379                 int rc = ext4_block_get(bg_ref->fs->bdev, &block, fblock);
380                 if (rc != EOK)
381                         return rc;
382
383                 memset(block.data, 0, block_size);
384                 block.dirty = true;
385
386                 ext4_block_set(bg_ref->fs->bdev, &block);
387                 if (rc != EOK)
388                         return rc;
389         }
390
391         return EOK;
392 }
393
394 static uint64_t ext4_fs_get_descriptor_block(struct ext4_sblock *s,
395                                              uint32_t bgid,
396                                              uint32_t dsc_per_block)
397 {
398         uint32_t first_meta_bg, dsc_id;
399
400         int has_super = 0;
401
402         dsc_id = bgid / dsc_per_block;
403         first_meta_bg = ext4_sb_first_meta_bg(s);
404
405         if (!ext4_sb_has_feature_incompatible(s,
406                                               EXT4_FEATURE_INCOMPAT_META_BG) ||
407             dsc_id < first_meta_bg)
408                 return ext4_get32(s, first_data_block) + dsc_id + 1;
409
410         if (ext4_sb_is_super_in_bg(s, bgid))
411                 has_super = 1;
412
413         return (has_super + ext4_fs_first_bg_block_no(s, bgid));
414 }
415
416 int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
417                                 struct ext4_block_group_ref *ref)
418 {
419         /* Compute number of descriptors, that fits in one data block */
420         uint32_t dsc_per_block =
421             ext4_sb_get_block_size(&fs->sb) / ext4_sb_get_desc_size(&fs->sb);
422
423         /* Block group descriptor table starts at the next block after
424          * superblock */
425         uint64_t block_id =
426             ext4_fs_get_descriptor_block(&fs->sb, bgid, dsc_per_block);
427
428         uint32_t offset =
429             (bgid % dsc_per_block) * ext4_sb_get_desc_size(&fs->sb);
430
431         int rc = ext4_block_get(fs->bdev, &ref->block, block_id);
432         if (rc != EOK)
433                 return rc;
434
435         ref->block_group = (void *)(ref->block.data + offset);
436         ref->fs = fs;
437         ref->index = bgid;
438         ref->dirty = false;
439
440         if (ext4_bg_has_flag(ref->block_group, EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
441                 rc = ext4_fs_init_block_bitmap(ref);
442                 if (rc != EOK) {
443                         ext4_block_set(fs->bdev, &ref->block);
444                         return rc;
445                 }
446                 ext4_bg_clear_flag(ref->block_group,
447                                    EXT4_BLOCK_GROUP_BLOCK_UNINIT);
448
449                 ref->dirty = true;
450         }
451
452         if (ext4_bg_has_flag(ref->block_group, EXT4_BLOCK_GROUP_INODE_UNINIT)) {
453                 rc = ext4_fs_init_inode_bitmap(ref);
454                 if (rc != EOK) {
455                         ext4_block_set(ref->fs->bdev, &ref->block);
456                         return rc;
457                 }
458
459                 ext4_bg_clear_flag(ref->block_group,
460                                    EXT4_BLOCK_GROUP_INODE_UNINIT);
461
462                 if (!ext4_bg_has_flag(ref->block_group,
463                                       EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
464                         rc = ext4_fs_init_inode_table(ref);
465                         if (rc != EOK) {
466                                 ext4_block_set(fs->bdev, &ref->block);
467                                 return rc;
468                         }
469
470                         ext4_bg_set_flag(ref->block_group,
471                                          EXT4_BLOCK_GROUP_ITABLE_ZEROED);
472                 }
473
474                 ref->dirty = true;
475         }
476
477         return EOK;
478 }
479
480 /**@brief  Compute checksum of block group descriptor.
481  * @param sb   Superblock
482  * @param bgid Index of block group in the filesystem
483  * @param bg   Block group to compute checksum for
484  * @return Checksum value
485  */
486 static uint16_t ext4_fs_bg_checksum(struct ext4_sblock *sb, uint32_t bgid,
487                                     struct ext4_bgroup *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_sb_has_feature_read_only(sb,
494                                           EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
495                 uint8_t *base = (uint8_t *)bg;
496                 uint8_t *checksum = (uint8_t *)&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 = to_le32(bgid);
502
503                 /* Initialization */
504                 crc = ext4_bg_crc16(~0, sb->uuid, sizeof(sb->uuid));
505
506                 /* Include index of block group */
507                 crc =
508                     ext4_bg_crc16(crc, (uint8_t *)&le_group, sizeof(le_group));
509
510                 /* Compute crc from the first part (stop before checksum field)
511                  */
512                 crc = ext4_bg_crc16(crc, (uint8_t *)bg, offset);
513
514                 /* Skip checksum */
515                 offset += sizeof(bg->checksum);
516
517                 /* Checksum of the rest of block group descriptor */
518                 if ((ext4_sb_has_feature_incompatible(
519                         sb, EXT4_FEATURE_INCOMPAT_64BIT)) &&
520                     (offset < ext4_sb_get_desc_size(sb)))
521
522                         crc = ext4_bg_crc16(crc, ((uint8_t *)bg) + offset,
523                                             ext4_sb_get_desc_size(sb) - offset);
524         }
525         return crc;
526 }
527
528 int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref)
529 {
530         /* Check if reference modified */
531         if (ref->dirty) {
532                 /* Compute new checksum of block group */
533                 uint16_t checksum = ext4_fs_bg_checksum(
534                     &ref->fs->sb, ref->index, ref->block_group);
535
536                 ref->block_group->checksum = to_le16(checksum);
537
538                 /* Mark block dirty for writing changes to physical device */
539                 ref->block.dirty = true;
540         }
541
542         /* Put back block, that contains block group descriptor */
543         return ext4_block_set(ref->fs->bdev, &ref->block);
544 }
545
546 int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
547                           struct ext4_inode_ref *ref)
548 {
549         /* Compute number of i-nodes, that fits in one data block */
550         uint32_t inodes_per_group = ext4_get32(&fs->sb, inodes_per_group);
551
552         /*
553          * Inode numbers are 1-based, but it is simpler to work with 0-based
554          * when computing indices
555          */
556         index -= 1;
557         uint32_t block_group = index / inodes_per_group;
558         uint32_t offset_in_group = index % inodes_per_group;
559
560         /* Load block group, where i-node is located */
561         struct ext4_block_group_ref bg_ref;
562
563         int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
564         if (rc != EOK) {
565                 return rc;
566         }
567
568         /* Load block address, where i-node table is located */
569         uint32_t inode_table_start =
570             ext4_bg_get_inode_table_first_block(bg_ref.block_group, &fs->sb);
571
572         /* Put back block group reference (not needed more) */
573         rc = ext4_fs_put_block_group_ref(&bg_ref);
574         if (rc != EOK) {
575                 return rc;
576         }
577
578         /* Compute position of i-node in the block group */
579         uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
580         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
581         uint32_t byte_offset_in_group = offset_in_group * inode_size;
582
583         /* Compute block address */
584         uint64_t block_id =
585             inode_table_start + (byte_offset_in_group / block_size);
586
587         rc = ext4_block_get(fs->bdev, &ref->block, block_id);
588         if (rc != EOK) {
589                 return rc;
590         }
591
592         /* Compute position of i-node in the data block */
593         uint32_t offset_in_block = byte_offset_in_group % block_size;
594         ref->inode = (struct ext4_inode *)(ref->block.data + offset_in_block);
595
596         /* We need to store the original value of index in the reference */
597         ref->index = index + 1;
598         ref->fs = fs;
599         ref->dirty = false;
600
601         return EOK;
602 }
603
604 int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref)
605 {
606         /* Check if reference modified */
607         if (ref->dirty) {
608                 /* Mark block dirty for writing changes to physical device */
609                 ref->block.dirty = true;
610         }
611
612         /* Put back block, that contains i-node */
613         return ext4_block_set(ref->fs->bdev, &ref->block);
614 }
615
616 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
617                         bool is_directory)
618 {
619         /* Check if newly allocated i-node will be a directory */
620         uint32_t i;
621         bool is_dir;
622
623         is_dir = is_directory;
624
625         /* Allocate inode by allocation algorithm */
626         uint32_t index;
627         int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
628         if (rc != EOK)
629                 return rc;
630
631         /* Load i-node from on-disk i-node table */
632         rc = ext4_fs_get_inode_ref(fs, index, inode_ref);
633         if (rc != EOK) {
634                 ext4_ialloc_free_inode(fs, index, is_dir);
635                 return rc;
636         }
637
638         /* Initialize i-node */
639         struct ext4_inode *inode = inode_ref->inode;
640
641         uint16_t mode;
642         if (is_dir) {
643                 /*
644                  * Default directory permissions to be compatible with other
645                  * systems
646                  * 0777 (octal) == rwxrwxrwx
647                  */
648
649                 mode = 0777;
650                 mode |= EXT4_INODE_MODE_DIRECTORY;
651                 ext4_inode_set_mode(&fs->sb, inode, mode);
652         } else {
653                 /*
654                  * Default file permissions to be compatible with other systems
655                  * 0666 (octal) == rw-rw-rw-
656                  */
657
658                 mode = 0666;
659                 mode |= EXT4_INODE_MODE_FILE;
660                 ext4_inode_set_mode(&fs->sb, inode, mode);
661         }
662
663         ext4_inode_set_links_count(inode, 0);
664         ext4_inode_set_uid(inode, 0);
665         ext4_inode_set_gid(inode, 0);
666         ext4_inode_set_size(inode, 0);
667         ext4_inode_set_access_time(inode, 0);
668         ext4_inode_set_change_inode_time(inode, 0);
669         ext4_inode_set_modification_time(inode, 0);
670         ext4_inode_set_deletion_time(inode, 0);
671         ext4_inode_set_blocks_count(&fs->sb, inode, 0);
672         ext4_inode_set_flags(inode, 0);
673         ext4_inode_set_generation(inode, 0);
674
675         /* Reset blocks array */
676         for (i = 0; i < EXT4_INODE_BLOCKS; i++)
677                 inode->blocks[i] = 0;
678
679 #if CONFIG_EXTENT_ENABLE
680         /* Initialize extents if needed */
681         if (ext4_sb_has_feature_incompatible(&fs->sb,
682                                              EXT4_FEATURE_INCOMPAT_EXTENTS)) {
683                 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
684
685                 /* Initialize extent root header */
686                 struct ext4_extent_header *header =
687                     ext4_inode_get_extent_header(inode);
688                 ext4_extent_header_set_depth(header, 0);
689                 ext4_extent_header_set_entries_count(header, 0);
690                 ext4_extent_header_set_generation(header, 0);
691                 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
692
693                 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
694                                         sizeof(struct ext4_extent_header)) /
695                                        sizeof(struct ext4_extent);
696
697                 ext4_extent_header_set_max_entries_count(header, max_entries);
698         }
699 #endif
700
701         inode_ref->dirty = true;
702
703         return EOK;
704 }
705
706 int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
707 {
708         struct ext4_fs *fs = inode_ref->fs;
709         uint32_t offset;
710         uint32_t suboffset;
711 #if CONFIG_EXTENT_ENABLE
712         /* For extents must be data block destroyed by other way */
713         if ((ext4_sb_has_feature_incompatible(&fs->sb,
714                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
715             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
716                 /* Data structures are released during truncate operation... */
717                 goto finish;
718         }
719 #endif
720         /* Release all indirect (no data) blocks */
721
722         /* 1) Single indirect */
723         uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
724         if (fblock != 0) {
725                 int rc = ext4_balloc_free_block(inode_ref, fblock);
726                 if (rc != EOK)
727                         return rc;
728
729                 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
730         }
731
732         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
733         uint32_t count = block_size / sizeof(uint32_t);
734
735         struct ext4_block block;
736
737         /* 2) Double indirect */
738         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
739         if (fblock != 0) {
740                 int rc = ext4_block_get(fs->bdev, &block, fblock);
741                 if (rc != EOK)
742                         return rc;
743
744                 uint32_t ind_block;
745                 for (offset = 0; offset < count; ++offset) {
746                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
747
748                         if (ind_block != 0) {
749                                 rc = ext4_balloc_free_block(inode_ref,
750                                                             ind_block);
751                                 if (rc != EOK) {
752                                         ext4_block_set(fs->bdev, &block);
753                                         return rc;
754                                 }
755                         }
756                 }
757
758                 ext4_block_set(fs->bdev, &block);
759                 rc = ext4_balloc_free_block(inode_ref, fblock);
760                 if (rc != EOK)
761                         return rc;
762
763                 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
764         }
765
766         /* 3) Tripple indirect */
767         struct ext4_block subblock;
768         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
769         if (fblock != 0) {
770                 int rc = ext4_block_get(fs->bdev, &block, fblock);
771                 if (rc != EOK)
772                         return rc;
773
774                 uint32_t ind_block;
775                 for (offset = 0; offset < count; ++offset) {
776                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
777
778                         if (ind_block != 0) {
779                                 rc = ext4_block_get(fs->bdev, &subblock,
780                                                     ind_block);
781                                 if (rc != EOK) {
782                                         ext4_block_set(fs->bdev, &block);
783                                         return rc;
784                                 }
785
786                                 uint32_t ind_subblock;
787                                 for (suboffset = 0; suboffset < count;
788                                      ++suboffset) {
789                                         ind_subblock = to_le32(
790                                             ((uint32_t *)
791                                                  subblock.data)[suboffset]);
792
793                                         if (ind_subblock != 0) {
794                                                 rc = ext4_balloc_free_block(
795                                                     inode_ref, ind_subblock);
796                                                 if (rc != EOK) {
797                                                         ext4_block_set(
798                                                             fs->bdev,
799                                                             &subblock);
800                                                         ext4_block_set(fs->bdev,
801                                                                        &block);
802                                                         return rc;
803                                                 }
804                                         }
805                                 }
806
807                                 ext4_block_set(fs->bdev, &subblock);
808
809                                 rc = ext4_balloc_free_block(inode_ref,
810                                                             ind_block);
811                                 if (rc != EOK) {
812                                         ext4_block_set(fs->bdev, &block);
813                                         return rc;
814                                 }
815                         }
816                 }
817
818                 ext4_block_set(fs->bdev, &block);
819                 rc = ext4_balloc_free_block(inode_ref, fblock);
820                 if (rc != EOK)
821                         return rc;
822
823                 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
824         }
825 #if CONFIG_EXTENT_ENABLE
826 finish:
827 #endif
828         /* Mark inode dirty for writing to the physical device */
829         inode_ref->dirty = true;
830
831         /* Free block with extended attributes if present */
832         uint32_t xattr_block =
833             ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
834         if (xattr_block) {
835                 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
836                 if (rc != EOK)
837                         return rc;
838
839                 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
840         }
841
842         /* Free inode by allocator */
843         int rc;
844         if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
845                                EXT4_INODE_MODE_DIRECTORY))
846                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
847         else
848                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
849
850         return rc;
851 }
852
853 int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
854 {
855         struct ext4_sblock *sb = &inode_ref->fs->sb;
856         uint32_t i;
857
858         /* Check flags, if i-node can be truncated */
859         if (!ext4_inode_can_truncate(sb, inode_ref->inode))
860                 return EINVAL;
861
862         /* If sizes are equal, nothing has to be done. */
863         uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
864         if (old_size == new_size)
865                 return EOK;
866
867         /* It's not supported to make the larger file by truncate operation */
868         if (old_size < new_size)
869                 return EINVAL;
870
871         /* Compute how many blocks will be released */
872         uint64_t size_diff = old_size - new_size;
873         uint32_t block_size = ext4_sb_get_block_size(sb);
874         uint32_t diff_blocks_count = size_diff / block_size;
875         if (size_diff % block_size != 0)
876                 diff_blocks_count++;
877
878         uint32_t old_blocks_count = old_size / block_size;
879         if (old_size % block_size != 0)
880                 old_blocks_count++;
881 #if CONFIG_EXTENT_ENABLE
882         if ((ext4_sb_has_feature_incompatible(sb,
883                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
884             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
885
886                 /* Extents require special operation */
887                 int rc = ext4_extent_release_blocks_from(
888                     inode_ref, old_blocks_count - diff_blocks_count);
889                 if (rc != EOK)
890                         return rc;
891         } else
892 #endif
893         {
894                 /* Release data blocks from the end of file */
895
896                 /* Starting from 1 because of logical blocks are numbered from 0
897                  */
898                 for (i = 1; i <= diff_blocks_count; ++i) {
899                         int rc = ext4_fs_release_inode_block(
900                             inode_ref, old_blocks_count - i);
901                         if (rc != EOK)
902                                 return rc;
903                 }
904         }
905
906         /* Update i-node */
907         ext4_inode_set_size(inode_ref->inode, new_size);
908         inode_ref->dirty = true;
909
910         return EOK;
911 }
912
913 int ext4_fs_get_inode_data_block_index(struct ext4_inode_ref *inode_ref,
914                                        uint64_t iblock, uint32_t *fblock)
915 {
916         struct ext4_fs *fs = inode_ref->fs;
917
918         /* For empty file is situation simple */
919         if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
920                 *fblock = 0;
921                 return EOK;
922         }
923
924         uint32_t current_block;
925 #if CONFIG_EXTENT_ENABLE
926         /* Handle i-node using extents */
927         if ((ext4_sb_has_feature_incompatible(&fs->sb,
928                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
929             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
930
931                 int rc =
932                     ext4_extent_find_block(inode_ref, iblock, &current_block);
933                 if (rc != EOK)
934                         return rc;
935
936                 *fblock = current_block;
937                 return EOK;
938         }
939 #endif
940
941         struct ext4_inode *inode = inode_ref->inode;
942
943         /* Direct block are read directly from array in i-node structure */
944         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
945                 current_block =
946                     ext4_inode_get_direct_block(inode, (uint32_t)iblock);
947                 *fblock = current_block;
948                 return EOK;
949         }
950
951         /* Determine indirection level of the target block */
952         unsigned int level = 0;
953         unsigned int i;
954         for (i = 1; i < 4; i++) {
955                 if (iblock < fs->inode_block_limits[i]) {
956                         level = i;
957                         break;
958                 }
959         }
960
961         if (level == 0)
962                 return EIO;
963
964         /* Compute offsets for the topmost level */
965         uint64_t block_offset_in_level =
966             iblock - fs->inode_block_limits[level - 1];
967         current_block = ext4_inode_get_indirect_block(inode, level - 1);
968         uint32_t offset_in_block =
969             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
970
971         /* Sparse file */
972         if (current_block == 0) {
973                 *fblock = 0;
974                 return EOK;
975         }
976
977         struct ext4_block block;
978
979         /*
980          * Navigate through other levels, until we find the block number
981          * or find null reference meaning we are dealing with sparse file
982          */
983         while (level > 0) {
984                 /* Load indirect block */
985                 int rc = ext4_block_get(fs->bdev, &block, current_block);
986                 if (rc != EOK)
987                         return rc;
988
989                 /* Read block address from indirect block */
990                 current_block =
991                     to_le32(((uint32_t *)block.data)[offset_in_block]);
992
993                 /* Put back indirect block untouched */
994                 rc = ext4_block_set(fs->bdev, &block);
995                 if (rc != EOK)
996                         return rc;
997
998                 /* Check for sparse file */
999                 if (current_block == 0) {
1000                         *fblock = 0;
1001                         return EOK;
1002                 }
1003
1004                 /* Jump to the next level */
1005                 level--;
1006
1007                 /* Termination condition - we have address of data block loaded
1008                  */
1009                 if (level == 0)
1010                         break;
1011
1012                 /* Visit the next level */
1013                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1014                 offset_in_block = block_offset_in_level /
1015                                   fs->inode_blocks_per_level[level - 1];
1016         }
1017
1018         *fblock = current_block;
1019
1020         return EOK;
1021 }
1022
1023 int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1024                                        uint64_t iblock, uint32_t fblock)
1025 {
1026         struct ext4_fs *fs = inode_ref->fs;
1027
1028 #if CONFIG_EXTENT_ENABLE
1029         /* Handle inode using extents */
1030         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1031                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1032             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1033                 /* Not reachable */
1034                 return ENOTSUP;
1035         }
1036 #endif
1037
1038         /* Handle simple case when we are dealing with direct reference */
1039         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1040                 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
1041                                             fblock);
1042                 inode_ref->dirty = true;
1043
1044                 return EOK;
1045         }
1046
1047         /* Determine the indirection level needed to get the desired block */
1048         unsigned int level = 0;
1049         unsigned int i;
1050         for (i = 1; i < 4; i++) {
1051                 if (iblock < fs->inode_block_limits[i]) {
1052                         level = i;
1053                         break;
1054                 }
1055         }
1056
1057         if (level == 0)
1058                 return EIO;
1059
1060         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
1061
1062         /* Compute offsets for the topmost level */
1063         uint64_t block_offset_in_level =
1064             iblock - fs->inode_block_limits[level - 1];
1065         uint32_t current_block =
1066             ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1067         uint32_t offset_in_block =
1068             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1069
1070         uint32_t new_block_addr;
1071
1072         struct ext4_block block;
1073         struct ext4_block new_block;
1074
1075         /* Is needed to allocate indirect block on the i-node level */
1076         if (current_block == 0) {
1077                 /* Allocate new indirect block */
1078                 int rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1079                 if (rc != EOK)
1080                         return rc;
1081
1082                 /* Update i-node */
1083                 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1084                                               new_block_addr);
1085                 inode_ref->dirty = true;
1086
1087                 /* Load newly allocated block */
1088                 rc = ext4_block_get(fs->bdev, &new_block, new_block_addr);
1089                 if (rc != EOK) {
1090                         ext4_balloc_free_block(inode_ref, new_block_addr);
1091                         return rc;
1092                 }
1093
1094                 /* Initialize new block */
1095                 memset(new_block.data, 0, block_size);
1096                 new_block.dirty = true;
1097
1098                 /* Put back the allocated block */
1099                 rc = ext4_block_set(fs->bdev, &new_block);
1100                 if (rc != EOK)
1101                         return rc;
1102
1103                 current_block = new_block_addr;
1104         }
1105
1106         /*
1107          * Navigate through other levels, until we find the block number
1108          * or find null reference meaning we are dealing with sparse file
1109          */
1110         while (level > 0) {
1111                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1112                 if (rc != EOK)
1113                         return rc;
1114
1115                 current_block =
1116                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1117
1118                 if ((level > 1) && (current_block == 0)) {
1119                         /* Allocate new block */
1120                         rc =
1121                             ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1122                         if (rc != EOK) {
1123                                 ext4_block_set(fs->bdev, &block);
1124                                 return rc;
1125                         }
1126
1127                         /* Load newly allocated block */
1128                         rc = ext4_block_get(fs->bdev, &new_block,
1129                                             new_block_addr);
1130
1131                         if (rc != EOK) {
1132                                 ext4_block_set(fs->bdev, &block);
1133                                 return rc;
1134                         }
1135
1136                         /* Initialize allocated block */
1137                         memset(new_block.data, 0, block_size);
1138                         new_block.dirty = true;
1139
1140                         rc = ext4_block_set(fs->bdev, &new_block);
1141                         if (rc != EOK) {
1142                                 ext4_block_set(fs->bdev, &block);
1143                                 return rc;
1144                         }
1145
1146                         /* Write block address to the parent */
1147                         ((uint32_t *)block.data)[offset_in_block] =
1148                             to_le32(new_block_addr);
1149                         block.dirty = true;
1150                         current_block = new_block_addr;
1151                 }
1152
1153                 /* Will be finished, write the fblock address */
1154                 if (level == 1) {
1155                         ((uint32_t *)block.data)[offset_in_block] =
1156                             to_le32(fblock);
1157                         block.dirty = true;
1158                 }
1159
1160                 rc = ext4_block_set(fs->bdev, &block);
1161                 if (rc != EOK)
1162                         return rc;
1163
1164                 level--;
1165
1166                 /*
1167                  * If we are on the last level, break here as
1168                  * there is no next level to visit
1169                  */
1170                 if (level == 0)
1171                         break;
1172
1173                 /* Visit the next level */
1174                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1175                 offset_in_block = block_offset_in_level /
1176                                   fs->inode_blocks_per_level[level - 1];
1177         }
1178
1179         return EOK;
1180 }
1181
1182 int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
1183                                 uint32_t iblock)
1184 {
1185         uint32_t fblock;
1186
1187         struct ext4_fs *fs = inode_ref->fs;
1188
1189         /* Extents are handled otherwise = there is not support in this function
1190          */
1191         ext4_assert(!(
1192             ext4_sb_has_feature_incompatible(&fs->sb,
1193                                              EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1194             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1195
1196         struct ext4_inode *inode = inode_ref->inode;
1197
1198         /* Handle simple case when we are dealing with direct reference */
1199         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1200                 fblock = ext4_inode_get_direct_block(inode, iblock);
1201
1202                 /* Sparse file */
1203                 if (fblock == 0)
1204                         return EOK;
1205
1206                 ext4_inode_set_direct_block(inode, iblock, 0);
1207                 return ext4_balloc_free_block(inode_ref, fblock);
1208         }
1209
1210         /* Determine the indirection level needed to get the desired block */
1211         unsigned int level = 0;
1212         unsigned int i;
1213         for (i = 1; i < 4; i++) {
1214                 if (iblock < fs->inode_block_limits[i]) {
1215                         level = i;
1216                         break;
1217                 }
1218         }
1219
1220         if (level == 0)
1221                 return EIO;
1222
1223         /* Compute offsets for the topmost level */
1224         uint64_t block_offset_in_level =
1225             iblock - fs->inode_block_limits[level - 1];
1226         uint32_t current_block =
1227             ext4_inode_get_indirect_block(inode, level - 1);
1228         uint32_t offset_in_block =
1229             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1230
1231         /*
1232          * Navigate through other levels, until we find the block number
1233          * or find null reference meaning we are dealing with sparse file
1234          */
1235         struct ext4_block block;
1236
1237         while (level > 0) {
1238
1239                 /* Sparse check */
1240                 if (current_block == 0)
1241                         return EOK;
1242
1243                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1244                 if (rc != EOK)
1245                         return rc;
1246
1247                 current_block =
1248                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1249
1250                 /* Set zero if physical data block address found */
1251                 if (level == 1) {
1252                         ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
1253                         block.dirty = true;
1254                 }
1255
1256                 rc = ext4_block_set(fs->bdev, &block);
1257                 if (rc != EOK)
1258                         return rc;
1259
1260                 level--;
1261
1262                 /*
1263                  * If we are on the last level, break here as
1264                  * there is no next level to visit
1265                  */
1266                 if (level == 0)
1267                         break;
1268
1269                 /* Visit the next level */
1270                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1271                 offset_in_block = block_offset_in_level /
1272                                   fs->inode_blocks_per_level[level - 1];
1273         }
1274
1275         fblock = current_block;
1276         if (fblock == 0)
1277                 return EOK;
1278
1279         /* Physical block is not referenced, it can be released */
1280         return ext4_balloc_free_block(inode_ref, fblock);
1281 }
1282
1283 int ext4_fs_append_inode_block(struct ext4_inode_ref *inode_ref,
1284                                uint32_t *fblock, uint32_t *iblock)
1285 {
1286 #if CONFIG_EXTENT_ENABLE
1287         /* Handle extents separately */
1288         if ((ext4_sb_has_feature_incompatible(&inode_ref->fs->sb,
1289                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1290             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1291                 return ext4_extent_append_block(inode_ref, iblock, fblock,
1292                                                 true);
1293         }
1294 #endif
1295         struct ext4_sblock *sb = &inode_ref->fs->sb;
1296
1297         /* Compute next block index and allocate data block */
1298         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1299         uint32_t block_size = ext4_sb_get_block_size(sb);
1300
1301         /* Align size i-node size */
1302         if ((inode_size % block_size) != 0)
1303                 inode_size += block_size - (inode_size % block_size);
1304
1305         /* Logical blocks are numbered from 0 */
1306         uint32_t new_block_idx = inode_size / block_size;
1307
1308         /* Allocate new physical block */
1309         uint32_t phys_block;
1310         int rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1311         if (rc != EOK)
1312                 return rc;
1313
1314         /* Add physical block address to the i-node */
1315         rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
1316                                                 phys_block);
1317         if (rc != EOK) {
1318                 ext4_balloc_free_block(inode_ref, phys_block);
1319                 return rc;
1320         }
1321
1322         /* Update i-node */
1323         ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1324         inode_ref->dirty = true;
1325
1326         *fblock = phys_block;
1327         *iblock = new_block_idx;
1328
1329         return EOK;
1330 }
1331
1332 void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
1333 {
1334         uint16_t link;
1335
1336         link = ext4_inode_get_links_count(inode_ref->inode);
1337         link++;
1338         ext4_inode_set_links_count(inode_ref->inode, link);
1339
1340         bool is_dx =
1341             ext4_sb_has_feature_compatible(&inode_ref->fs->sb,
1342                                            EXT4_FEATURE_COMPAT_DIR_INDEX) &&
1343             ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
1344
1345         if (is_dx && link > 1) {
1346                 if (link >= EXT4_LINK_MAX || link == 2) {
1347                         ext4_inode_set_links_count(inode_ref->inode, 1);
1348
1349                         uint32_t v =
1350                             ext4_get32(&inode_ref->fs->sb, features_read_only);
1351                         v |= EXT4_FEATURE_RO_COMPAT_DIR_NLINK;
1352                         ext4_set32(&inode_ref->fs->sb, features_read_only, v);
1353                 }
1354         }
1355 }
1356
1357 void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
1358 {
1359         uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
1360         if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
1361                                 EXT4_INODE_MODE_DIRECTORY)) {
1362                 if (links > 0)
1363                         ext4_inode_set_links_count(inode_ref->inode, links - 1);
1364                 return;
1365         }
1366
1367         if (links > 2)
1368                 ext4_inode_set_links_count(inode_ref->inode, links - 1);
1369 }
1370
1371 /**
1372  * @}
1373  */