since EXT4_DIRECTORY_FILETYPE_* are duplication of EXT4_DIRENTRY_*, the former is...
[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 void ext4_fs_inode_blocks_init(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref)
617 {
618         int i;
619         struct ext4_inode *inode = inode_ref->inode;
620
621         for (i = 0; i < EXT4_INODE_BLOCKS; i++)
622                 inode->blocks[i] = 0;
623
624 #if CONFIG_EXTENT_ENABLE
625         /* Initialize extents if needed */
626         if (ext4_sb_has_feature_incompatible(&fs->sb,
627                                 EXT4_FEATURE_INCOMPAT_EXTENTS)) {
628                 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
629
630                 /* Initialize extent root header */
631                 struct ext4_extent_header *header = ext4_inode_get_extent_header(inode);
632                 ext4_extent_header_set_depth(header, 0);
633                 ext4_extent_header_set_entries_count(header, 0);
634                 ext4_extent_header_set_generation(header, 0);
635                 ext4_extent_header_set_magic(header, EXT4_EXTENT_MAGIC);
636
637                 uint16_t max_entries = (EXT4_INODE_BLOCKS * sizeof(uint32_t) -
638                                 sizeof(struct ext4_extent_header)) /
639                         sizeof(struct ext4_extent);
640
641                 ext4_extent_header_set_max_entries_count(header, max_entries);
642         }
643 #endif
644 }
645
646 static uint32_t ext4_fs_correspond_inode_mode(int filetype)
647 {
648         switch (filetype) {
649         case EXT4_DIRENTRY_DIR:
650                 return EXT4_INODE_MODE_DIRECTORY;
651         case EXT4_DIRENTRY_REG_FILE:
652                 return EXT4_INODE_MODE_FILE;
653         case EXT4_DIRENTRY_SYMLINK:
654                 return EXT4_INODE_MODE_SOFTLINK;
655         default:
656                 /* FIXME: right now we only support 3 file type. */
657                 ext4_assert(0);
658         }
659         return 0;
660 }
661
662 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
663                         int filetype)
664 {
665         /* Check if newly allocated i-node will be a directory */
666         bool is_dir;
667
668         is_dir = (filetype == EXT4_DIRENTRY_DIR);
669
670         /* Allocate inode by allocation algorithm */
671         uint32_t index;
672         int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
673         if (rc != EOK)
674                 return rc;
675
676         /* Load i-node from on-disk i-node table */
677         rc = ext4_fs_get_inode_ref(fs, index, inode_ref);
678         if (rc != EOK) {
679                 ext4_ialloc_free_inode(fs, index, is_dir);
680                 return rc;
681         }
682
683         /* Initialize i-node */
684         struct ext4_inode *inode = inode_ref->inode;
685
686         uint32_t mode;
687         if (is_dir) {
688                 /*
689                  * Default directory permissions to be compatible with other
690                  * systems
691                  * 0777 (octal) == rwxrwxrwx
692                  */
693
694                 mode = 0777;
695                 mode |= EXT4_INODE_MODE_DIRECTORY;
696         } else {
697                 /*
698                  * Default file permissions to be compatible with other systems
699                  * 0666 (octal) == rw-rw-rw-
700                  */
701
702                 mode = 0666;
703                 mode |= ext4_fs_correspond_inode_mode(filetype);
704         }
705         ext4_inode_set_mode(&fs->sb, inode, mode);
706
707         ext4_inode_set_links_count(inode, 0);
708         ext4_inode_set_uid(inode, 0);
709         ext4_inode_set_gid(inode, 0);
710         ext4_inode_set_size(inode, 0);
711         ext4_inode_set_access_time(inode, 0);
712         ext4_inode_set_change_inode_time(inode, 0);
713         ext4_inode_set_modification_time(inode, 0);
714         ext4_inode_set_deletion_time(inode, 0);
715         ext4_inode_set_blocks_count(&fs->sb, inode, 0);
716         ext4_inode_set_flags(inode, 0);
717         ext4_inode_set_generation(inode, 0);
718
719         /* Reset blocks array. For symbolic link inode, just
720          * fill in blocks with 0 */
721         if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
722                 for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
723                         inode->blocks[i] = 0;
724
725         } else
726                 ext4_fs_inode_blocks_init(fs, inode_ref);
727
728         inode_ref->dirty = true;
729
730         return EOK;
731 }
732
733 int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
734 {
735         struct ext4_fs *fs = inode_ref->fs;
736         uint32_t offset;
737         uint32_t suboffset;
738 #if CONFIG_EXTENT_ENABLE
739         /* For extents must be data block destroyed by other way */
740         if ((ext4_sb_has_feature_incompatible(&fs->sb,
741                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
742             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
743                 /* Data structures are released during truncate operation... */
744                 goto finish;
745         }
746 #endif
747         /* Release all indirect (no data) blocks */
748
749         /* 1) Single indirect */
750         uint32_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
751         if (fblock != 0) {
752                 int rc = ext4_balloc_free_block(inode_ref, fblock);
753                 if (rc != EOK)
754                         return rc;
755
756                 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
757         }
758
759         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
760         uint32_t count = block_size / sizeof(uint32_t);
761
762         struct ext4_block block;
763
764         /* 2) Double indirect */
765         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
766         if (fblock != 0) {
767                 int rc = ext4_block_get(fs->bdev, &block, fblock);
768                 if (rc != EOK)
769                         return rc;
770
771                 uint32_t ind_block;
772                 for (offset = 0; offset < count; ++offset) {
773                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
774
775                         if (ind_block != 0) {
776                                 rc = ext4_balloc_free_block(inode_ref,
777                                                             ind_block);
778                                 if (rc != EOK) {
779                                         ext4_block_set(fs->bdev, &block);
780                                         return rc;
781                                 }
782                         }
783                 }
784
785                 ext4_block_set(fs->bdev, &block);
786                 rc = ext4_balloc_free_block(inode_ref, fblock);
787                 if (rc != EOK)
788                         return rc;
789
790                 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
791         }
792
793         /* 3) Tripple indirect */
794         struct ext4_block subblock;
795         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
796         if (fblock != 0) {
797                 int rc = ext4_block_get(fs->bdev, &block, fblock);
798                 if (rc != EOK)
799                         return rc;
800
801                 uint32_t ind_block;
802                 for (offset = 0; offset < count; ++offset) {
803                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
804
805                         if (ind_block != 0) {
806                                 rc = ext4_block_get(fs->bdev, &subblock,
807                                                     ind_block);
808                                 if (rc != EOK) {
809                                         ext4_block_set(fs->bdev, &block);
810                                         return rc;
811                                 }
812
813                                 uint32_t ind_subblock;
814                                 for (suboffset = 0; suboffset < count;
815                                      ++suboffset) {
816                                         ind_subblock = to_le32(
817                                             ((uint32_t *)
818                                                  subblock.data)[suboffset]);
819
820                                         if (ind_subblock != 0) {
821                                                 rc = ext4_balloc_free_block(
822                                                     inode_ref, ind_subblock);
823                                                 if (rc != EOK) {
824                                                         ext4_block_set(
825                                                             fs->bdev,
826                                                             &subblock);
827                                                         ext4_block_set(fs->bdev,
828                                                                        &block);
829                                                         return rc;
830                                                 }
831                                         }
832                                 }
833
834                                 ext4_block_set(fs->bdev, &subblock);
835
836                                 rc = ext4_balloc_free_block(inode_ref,
837                                                             ind_block);
838                                 if (rc != EOK) {
839                                         ext4_block_set(fs->bdev, &block);
840                                         return rc;
841                                 }
842                         }
843                 }
844
845                 ext4_block_set(fs->bdev, &block);
846                 rc = ext4_balloc_free_block(inode_ref, fblock);
847                 if (rc != EOK)
848                         return rc;
849
850                 ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
851         }
852 #if CONFIG_EXTENT_ENABLE
853 finish:
854 #endif
855         /* Mark inode dirty for writing to the physical device */
856         inode_ref->dirty = true;
857
858         /* Free block with extended attributes if present */
859         uint32_t xattr_block =
860             ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
861         if (xattr_block) {
862                 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
863                 if (rc != EOK)
864                         return rc;
865
866                 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
867         }
868
869         /* Free inode by allocator */
870         int rc;
871         if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
872                                EXT4_INODE_MODE_DIRECTORY))
873                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
874         else
875                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
876
877         return rc;
878 }
879
880 int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
881 {
882         struct ext4_sblock *sb = &inode_ref->fs->sb;
883         uint32_t i;
884
885         /* Check flags, if i-node can be truncated */
886         if (!ext4_inode_can_truncate(sb, inode_ref->inode))
887                 return EINVAL;
888
889         /* If sizes are equal, nothing has to be done. */
890         uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
891         if (old_size == new_size)
892                 return EOK;
893
894         /* It's not supported to make the larger file by truncate operation */
895         if (old_size < new_size)
896                 return EINVAL;
897
898         if (ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK)
899                         && old_size < sizeof(inode_ref->inode->blocks)
900                         && !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
901                 char *content = (char *)inode_ref->inode->blocks;
902                 memset(content + new_size, 0,
903                         sizeof(inode_ref->inode->blocks) - new_size);
904                 ext4_inode_set_size(inode_ref->inode, new_size);
905                 inode_ref->dirty = true;
906
907                 return EOK;
908         }
909
910         /* Compute how many blocks will be released */
911         uint64_t size_diff = old_size - new_size;
912         uint32_t block_size = ext4_sb_get_block_size(sb);
913         uint32_t diff_blocks_count = size_diff / block_size;
914         if (size_diff % block_size != 0)
915                 diff_blocks_count++;
916
917         uint32_t old_blocks_count = old_size / block_size;
918         if (old_size % block_size != 0)
919                 old_blocks_count++;
920 #if CONFIG_EXTENT_ENABLE
921         if ((ext4_sb_has_feature_incompatible(sb,
922                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
923             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
924
925                 /* Extents require special operation */
926                 int rc = ext4_extent_release_blocks_from(
927                     inode_ref, old_blocks_count - diff_blocks_count);
928                 if (rc != EOK)
929                         return rc;
930         } else
931 #endif
932         {
933                 /* Release data blocks from the end of file */
934
935                 /* Starting from 1 because of logical blocks are numbered from 0
936                  */
937                 for (i = 1; i <= diff_blocks_count; ++i) {
938                         int rc = ext4_fs_release_inode_block(
939                             inode_ref, old_blocks_count - i);
940                         if (rc != EOK)
941                                 return rc;
942                 }
943         }
944
945         /* Update i-node */
946         ext4_inode_set_size(inode_ref->inode, new_size);
947         inode_ref->dirty = true;
948
949         return EOK;
950 }
951
952 int ext4_fs_get_inode_data_block_index(struct ext4_inode_ref *inode_ref,
953                                        uint64_t iblock, uint32_t *fblock)
954 {
955         struct ext4_fs *fs = inode_ref->fs;
956
957         /* For empty file is situation simple */
958         if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
959                 *fblock = 0;
960                 return EOK;
961         }
962
963         uint32_t current_block;
964 #if CONFIG_EXTENT_ENABLE
965         /* Handle i-node using extents */
966         if ((ext4_sb_has_feature_incompatible(&fs->sb,
967                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
968             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
969
970                 int rc =
971                     ext4_extent_find_block(inode_ref, iblock, &current_block);
972                 if (rc != EOK)
973                         return rc;
974
975                 *fblock = current_block;
976                 return EOK;
977         }
978 #endif
979
980         struct ext4_inode *inode = inode_ref->inode;
981
982         /* Direct block are read directly from array in i-node structure */
983         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
984                 current_block =
985                     ext4_inode_get_direct_block(inode, (uint32_t)iblock);
986                 *fblock = current_block;
987                 return EOK;
988         }
989
990         /* Determine indirection level of the target block */
991         unsigned int level = 0;
992         unsigned int i;
993         for (i = 1; i < 4; i++) {
994                 if (iblock < fs->inode_block_limits[i]) {
995                         level = i;
996                         break;
997                 }
998         }
999
1000         if (level == 0)
1001                 return EIO;
1002
1003         /* Compute offsets for the topmost level */
1004         uint64_t block_offset_in_level =
1005             iblock - fs->inode_block_limits[level - 1];
1006         current_block = ext4_inode_get_indirect_block(inode, level - 1);
1007         uint32_t offset_in_block =
1008             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1009
1010         /* Sparse file */
1011         if (current_block == 0) {
1012                 *fblock = 0;
1013                 return EOK;
1014         }
1015
1016         struct ext4_block block;
1017
1018         /*
1019          * Navigate through other levels, until we find the block number
1020          * or find null reference meaning we are dealing with sparse file
1021          */
1022         while (level > 0) {
1023                 /* Load indirect block */
1024                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1025                 if (rc != EOK)
1026                         return rc;
1027
1028                 /* Read block address from indirect block */
1029                 current_block =
1030                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1031
1032                 /* Put back indirect block untouched */
1033                 rc = ext4_block_set(fs->bdev, &block);
1034                 if (rc != EOK)
1035                         return rc;
1036
1037                 /* Check for sparse file */
1038                 if (current_block == 0) {
1039                         *fblock = 0;
1040                         return EOK;
1041                 }
1042
1043                 /* Jump to the next level */
1044                 level--;
1045
1046                 /* Termination condition - we have address of data block loaded
1047                  */
1048                 if (level == 0)
1049                         break;
1050
1051                 /* Visit the next level */
1052                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1053                 offset_in_block = block_offset_in_level /
1054                                   fs->inode_blocks_per_level[level - 1];
1055         }
1056
1057         *fblock = current_block;
1058
1059         return EOK;
1060 }
1061
1062 int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1063                                        uint64_t iblock, uint32_t fblock)
1064 {
1065         struct ext4_fs *fs = inode_ref->fs;
1066
1067 #if CONFIG_EXTENT_ENABLE
1068         /* Handle inode using extents */
1069         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1070                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1071             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1072                 /* Not reachable */
1073                 return ENOTSUP;
1074         }
1075 #endif
1076
1077         /* Handle simple case when we are dealing with direct reference */
1078         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1079                 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
1080                                             fblock);
1081                 inode_ref->dirty = true;
1082
1083                 return EOK;
1084         }
1085
1086         /* Determine the indirection level needed to get the desired block */
1087         unsigned int level = 0;
1088         unsigned int i;
1089         for (i = 1; i < 4; i++) {
1090                 if (iblock < fs->inode_block_limits[i]) {
1091                         level = i;
1092                         break;
1093                 }
1094         }
1095
1096         if (level == 0)
1097                 return EIO;
1098
1099         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
1100
1101         /* Compute offsets for the topmost level */
1102         uint64_t block_offset_in_level =
1103             iblock - fs->inode_block_limits[level - 1];
1104         uint32_t current_block =
1105             ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1106         uint32_t offset_in_block =
1107             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1108
1109         uint32_t new_block_addr;
1110
1111         struct ext4_block block;
1112         struct ext4_block new_block;
1113
1114         /* Is needed to allocate indirect block on the i-node level */
1115         if (current_block == 0) {
1116                 /* Allocate new indirect block */
1117                 int rc = ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1118                 if (rc != EOK)
1119                         return rc;
1120
1121                 /* Update i-node */
1122                 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1123                                               new_block_addr);
1124                 inode_ref->dirty = true;
1125
1126                 /* Load newly allocated block */
1127                 rc = ext4_block_get(fs->bdev, &new_block, new_block_addr);
1128                 if (rc != EOK) {
1129                         ext4_balloc_free_block(inode_ref, new_block_addr);
1130                         return rc;
1131                 }
1132
1133                 /* Initialize new block */
1134                 memset(new_block.data, 0, block_size);
1135                 new_block.dirty = true;
1136
1137                 /* Put back the allocated block */
1138                 rc = ext4_block_set(fs->bdev, &new_block);
1139                 if (rc != EOK)
1140                         return rc;
1141
1142                 current_block = new_block_addr;
1143         }
1144
1145         /*
1146          * Navigate through other levels, until we find the block number
1147          * or find null reference meaning we are dealing with sparse file
1148          */
1149         while (level > 0) {
1150                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1151                 if (rc != EOK)
1152                         return rc;
1153
1154                 current_block =
1155                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1156
1157                 if ((level > 1) && (current_block == 0)) {
1158                         /* Allocate new block */
1159                         rc =
1160                             ext4_balloc_alloc_block(inode_ref, &new_block_addr);
1161                         if (rc != EOK) {
1162                                 ext4_block_set(fs->bdev, &block);
1163                                 return rc;
1164                         }
1165
1166                         /* Load newly allocated block */
1167                         rc = ext4_block_get(fs->bdev, &new_block,
1168                                             new_block_addr);
1169
1170                         if (rc != EOK) {
1171                                 ext4_block_set(fs->bdev, &block);
1172                                 return rc;
1173                         }
1174
1175                         /* Initialize allocated block */
1176                         memset(new_block.data, 0, block_size);
1177                         new_block.dirty = true;
1178
1179                         rc = ext4_block_set(fs->bdev, &new_block);
1180                         if (rc != EOK) {
1181                                 ext4_block_set(fs->bdev, &block);
1182                                 return rc;
1183                         }
1184
1185                         /* Write block address to the parent */
1186                         ((uint32_t *)block.data)[offset_in_block] =
1187                             to_le32(new_block_addr);
1188                         block.dirty = true;
1189                         current_block = new_block_addr;
1190                 }
1191
1192                 /* Will be finished, write the fblock address */
1193                 if (level == 1) {
1194                         ((uint32_t *)block.data)[offset_in_block] =
1195                             to_le32(fblock);
1196                         block.dirty = true;
1197                 }
1198
1199                 rc = ext4_block_set(fs->bdev, &block);
1200                 if (rc != EOK)
1201                         return rc;
1202
1203                 level--;
1204
1205                 /*
1206                  * If we are on the last level, break here as
1207                  * there is no next level to visit
1208                  */
1209                 if (level == 0)
1210                         break;
1211
1212                 /* Visit the next level */
1213                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1214                 offset_in_block = block_offset_in_level /
1215                                   fs->inode_blocks_per_level[level - 1];
1216         }
1217
1218         return EOK;
1219 }
1220
1221 int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
1222                                 uint32_t iblock)
1223 {
1224         uint32_t fblock;
1225
1226         struct ext4_fs *fs = inode_ref->fs;
1227
1228         /* Extents are handled otherwise = there is not support in this function
1229          */
1230         ext4_assert(!(
1231             ext4_sb_has_feature_incompatible(&fs->sb,
1232                                              EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1233             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1234
1235         struct ext4_inode *inode = inode_ref->inode;
1236
1237         /* Handle simple case when we are dealing with direct reference */
1238         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1239                 fblock = ext4_inode_get_direct_block(inode, iblock);
1240
1241                 /* Sparse file */
1242                 if (fblock == 0)
1243                         return EOK;
1244
1245                 ext4_inode_set_direct_block(inode, iblock, 0);
1246                 return ext4_balloc_free_block(inode_ref, fblock);
1247         }
1248
1249         /* Determine the indirection level needed to get the desired block */
1250         unsigned int level = 0;
1251         unsigned int i;
1252         for (i = 1; i < 4; i++) {
1253                 if (iblock < fs->inode_block_limits[i]) {
1254                         level = i;
1255                         break;
1256                 }
1257         }
1258
1259         if (level == 0)
1260                 return EIO;
1261
1262         /* Compute offsets for the topmost level */
1263         uint64_t block_offset_in_level =
1264             iblock - fs->inode_block_limits[level - 1];
1265         uint32_t current_block =
1266             ext4_inode_get_indirect_block(inode, level - 1);
1267         uint32_t offset_in_block =
1268             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1269
1270         /*
1271          * Navigate through other levels, until we find the block number
1272          * or find null reference meaning we are dealing with sparse file
1273          */
1274         struct ext4_block block;
1275
1276         while (level > 0) {
1277
1278                 /* Sparse check */
1279                 if (current_block == 0)
1280                         return EOK;
1281
1282                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1283                 if (rc != EOK)
1284                         return rc;
1285
1286                 current_block =
1287                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1288
1289                 /* Set zero if physical data block address found */
1290                 if (level == 1) {
1291                         ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
1292                         block.dirty = true;
1293                 }
1294
1295                 rc = ext4_block_set(fs->bdev, &block);
1296                 if (rc != EOK)
1297                         return rc;
1298
1299                 level--;
1300
1301                 /*
1302                  * If we are on the last level, break here as
1303                  * there is no next level to visit
1304                  */
1305                 if (level == 0)
1306                         break;
1307
1308                 /* Visit the next level */
1309                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1310                 offset_in_block = block_offset_in_level /
1311                                   fs->inode_blocks_per_level[level - 1];
1312         }
1313
1314         fblock = current_block;
1315         if (fblock == 0)
1316                 return EOK;
1317
1318         /* Physical block is not referenced, it can be released */
1319         return ext4_balloc_free_block(inode_ref, fblock);
1320 }
1321
1322 int ext4_fs_append_inode_block(struct ext4_inode_ref *inode_ref,
1323                                uint32_t *fblock, uint32_t *iblock)
1324 {
1325 #if CONFIG_EXTENT_ENABLE
1326         /* Handle extents separately */
1327         if ((ext4_sb_has_feature_incompatible(&inode_ref->fs->sb,
1328                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1329             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1330                 return ext4_extent_append_block(inode_ref, iblock, fblock,
1331                                                 true);
1332         }
1333 #endif
1334         struct ext4_sblock *sb = &inode_ref->fs->sb;
1335
1336         /* Compute next block index and allocate data block */
1337         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1338         uint32_t block_size = ext4_sb_get_block_size(sb);
1339
1340         /* Align size i-node size */
1341         if ((inode_size % block_size) != 0)
1342                 inode_size += block_size - (inode_size % block_size);
1343
1344         /* Logical blocks are numbered from 0 */
1345         uint32_t new_block_idx = inode_size / block_size;
1346
1347         /* Allocate new physical block */
1348         uint32_t phys_block;
1349         int rc = ext4_balloc_alloc_block(inode_ref, &phys_block);
1350         if (rc != EOK)
1351                 return rc;
1352
1353         /* Add physical block address to the i-node */
1354         rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
1355                                                 phys_block);
1356         if (rc != EOK) {
1357                 ext4_balloc_free_block(inode_ref, phys_block);
1358                 return rc;
1359         }
1360
1361         /* Update i-node */
1362         ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1363         inode_ref->dirty = true;
1364
1365         *fblock = phys_block;
1366         *iblock = new_block_idx;
1367
1368         return EOK;
1369 }
1370
1371 void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
1372 {
1373         uint16_t link;
1374
1375         link = ext4_inode_get_links_count(inode_ref->inode);
1376         link++;
1377         ext4_inode_set_links_count(inode_ref->inode, link);
1378
1379         bool is_dx =
1380             ext4_sb_has_feature_compatible(&inode_ref->fs->sb,
1381                                            EXT4_FEATURE_COMPAT_DIR_INDEX) &&
1382             ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
1383
1384         if (is_dx && link > 1) {
1385                 if (link >= EXT4_LINK_MAX || link == 2) {
1386                         ext4_inode_set_links_count(inode_ref->inode, 1);
1387
1388                         uint32_t v =
1389                             ext4_get32(&inode_ref->fs->sb, features_read_only);
1390                         v |= EXT4_FEATURE_RO_COMPAT_DIR_NLINK;
1391                         ext4_set32(&inode_ref->fs->sb, features_read_only, v);
1392                 }
1393         }
1394 }
1395
1396 void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
1397 {
1398         uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
1399         if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
1400                                 EXT4_INODE_MODE_DIRECTORY)) {
1401                 if (links > 0)
1402                         ext4_inode_set_links_count(inode_ref->inode, links - 1);
1403                 return;
1404         }
1405
1406         if (links > 2)
1407                 ext4_inode_set_links_count(inode_ref->inode, links - 1);
1408 }
1409
1410 /**
1411  * @}
1412  */