880268ee0c0f37cecf6e07011cba643efa0f95b6
[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_crc32c.h"
48 #include "ext4_debug.h"
49 #include "ext4_block_group.h"
50 #include "ext4_balloc.h"
51 #include "ext4_bitmap.h"
52 #include "ext4_inode.h"
53 #include "ext4_ialloc.h"
54 #include "ext4_extent.h"
55
56 #include <string.h>
57
58 int ext4_fs_init(struct ext4_fs *fs, struct ext4_blockdev *bdev)
59 {
60         int r, i;
61         uint16_t tmp;
62         uint32_t bsize;
63         bool read_only = false;
64
65         ext4_assert(fs && bdev);
66
67         fs->bdev = bdev;
68
69         r = ext4_sb_read(fs->bdev, &fs->sb);
70         if (r != EOK)
71                 return r;
72
73         if (!ext4_sb_check(&fs->sb))
74                 return ENOTSUP;
75
76         bsize = ext4_sb_get_block_size(&fs->sb);
77         if (bsize > EXT4_MAX_BLOCK_SIZE)
78                 return ENXIO;
79
80         r = ext4_fs_check_features(fs, &read_only);
81         if (r != EOK)
82                 return r;
83
84         if (read_only)
85                 return ENOTSUP;
86
87         /* Compute limits for indirect block levels */
88         uint32_t blocks_id = bsize / sizeof(uint32_t);
89
90         fs->inode_block_limits[0] = EXT4_INODE_DIRECT_BLOCK_COUNT;
91         fs->inode_blocks_per_level[0] = 1;
92
93         for (i = 1; i < 4; i++) {
94                 fs->inode_blocks_per_level[i] =
95                     fs->inode_blocks_per_level[i - 1] * blocks_id;
96                 fs->inode_block_limits[i] = fs->inode_block_limits[i - 1] +
97                                             fs->inode_blocks_per_level[i];
98         }
99
100         /*Validate FS*/
101         tmp = ext4_get16(&fs->sb, state);
102         if (tmp & EXT4_SUPERBLOCK_STATE_ERROR_FS)
103                 ext4_dbg(DEBUG_FS, DBG_WARN
104                                 "last umount error: superblock fs_error flag\n");
105
106
107         /* Mark system as mounted */
108         ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_ERROR_FS);
109         r = ext4_sb_write(fs->bdev, &fs->sb);
110         if (r != EOK)
111                 return r;
112
113         /*Update mount count*/
114         ext4_set16(&fs->sb, mount_count, ext4_get16(&fs->sb, mount_count) + 1);
115
116         return r;
117 }
118
119 int ext4_fs_fini(struct ext4_fs *fs)
120 {
121         ext4_assert(fs);
122
123         /*Set superblock state*/
124         ext4_set16(&fs->sb, state, EXT4_SUPERBLOCK_STATE_VALID_FS);
125
126         return ext4_sb_write(fs->bdev, &fs->sb);
127 }
128
129 static void ext4_fs_debug_features_inc(uint32_t features_incompatible)
130 {
131         if (features_incompatible & EXT4_FEATURE_INCOMPAT_COMPRESSION)
132                 ext4_dbg(DEBUG_FS, DBG_NONE "compression\n");
133         if (features_incompatible & EXT4_FEATURE_INCOMPAT_FILETYPE)
134                 ext4_dbg(DEBUG_FS, DBG_NONE "filetype\n");
135         if (features_incompatible & EXT4_FEATURE_INCOMPAT_RECOVER)
136                 ext4_dbg(DEBUG_FS, DBG_NONE "recover\n");
137         if (features_incompatible & EXT4_FEATURE_INCOMPAT_JOURNAL_DEV)
138                 ext4_dbg(DEBUG_FS, DBG_NONE "journal_dev\n");
139         if (features_incompatible & EXT4_FEATURE_INCOMPAT_META_BG)
140                 ext4_dbg(DEBUG_FS, DBG_NONE "meta_bg\n");
141         if (features_incompatible & EXT4_FEATURE_INCOMPAT_EXTENTS)
142                 ext4_dbg(DEBUG_FS, DBG_NONE "extents\n");
143         if (features_incompatible & EXT4_FEATURE_INCOMPAT_64BIT)
144                 ext4_dbg(DEBUG_FS, DBG_NONE "64bit\n");
145         if (features_incompatible & EXT4_FEATURE_INCOMPAT_MMP)
146                 ext4_dbg(DEBUG_FS, DBG_NONE "mnp\n");
147         if (features_incompatible & EXT4_FEATURE_INCOMPAT_FLEX_BG)
148                 ext4_dbg(DEBUG_FS, DBG_NONE "flex_bg\n");
149         if (features_incompatible & EXT4_FEATURE_INCOMPAT_EA_INODE)
150                 ext4_dbg(DEBUG_FS, DBG_NONE "ea_inode\n");
151         if (features_incompatible & EXT4_FEATURE_INCOMPAT_DIRDATA)
152                 ext4_dbg(DEBUG_FS, DBG_NONE "dirdata\n");
153         if (features_incompatible & EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM)
154                 ext4_dbg(DEBUG_FS, DBG_NONE "meta_csum\n");
155         if (features_incompatible & EXT4_FEATURE_INCOMPAT_LARGEDIR)
156                 ext4_dbg(DEBUG_FS, DBG_NONE "largedir\n");
157         if (features_incompatible & EXT4_FEATURE_INCOMPAT_INLINE_DATA)
158                 ext4_dbg(DEBUG_FS, DBG_NONE "inline_data\n");
159 }
160 static void ext4_fs_debug_features_comp(uint32_t features_compatible)
161 {
162         if (features_compatible & EXT4_FEATURE_COMPAT_DIR_PREALLOC)
163                 ext4_dbg(DEBUG_FS, DBG_NONE "dir_prealloc\n");
164         if (features_compatible & EXT4_FEATURE_COMPAT_IMAGIC_INODES)
165                 ext4_dbg(DEBUG_FS, DBG_NONE "imagic_inodes\n");
166         if (features_compatible & EXT4_FEATURE_COMPAT_HAS_JOURNAL)
167                 ext4_dbg(DEBUG_FS, DBG_NONE "has_journal\n");
168         if (features_compatible & EXT4_FEATURE_COMPAT_EXT_ATTR)
169                 ext4_dbg(DEBUG_FS, DBG_NONE "ext_attr\n");
170         if (features_compatible & EXT4_FEATURE_COMPAT_RESIZE_INODE)
171                 ext4_dbg(DEBUG_FS, DBG_NONE "resize_inode\n");
172         if (features_compatible & EXT4_FEATURE_COMPAT_DIR_INDEX)
173                 ext4_dbg(DEBUG_FS, DBG_NONE "dir_index\n");
174 }
175
176 static void ext4_fs_debug_features_ro(uint32_t features_ro)
177 {
178         if (features_ro & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER)
179                 ext4_dbg(DEBUG_FS, DBG_NONE "sparse_super\n");
180         if (features_ro & EXT4_FEATURE_RO_COMPAT_LARGE_FILE)
181                 ext4_dbg(DEBUG_FS, DBG_NONE "large_file\n");
182         if (features_ro & EXT4_FEATURE_RO_COMPAT_BTREE_DIR)
183                 ext4_dbg(DEBUG_FS, DBG_NONE "btree_dir\n");
184         if (features_ro & EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
185                 ext4_dbg(DEBUG_FS, DBG_NONE "huge_file\n");
186         if (features_ro & EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
187                 ext4_dbg(DEBUG_FS, DBG_NONE "gtd_csum\n");
188         if (features_ro & EXT4_FEATURE_RO_COMPAT_DIR_NLINK)
189                 ext4_dbg(DEBUG_FS, DBG_NONE "dir_nlink\n");
190         if (features_ro & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)
191                 ext4_dbg(DEBUG_FS, DBG_NONE "extra_isize\n");
192         if (features_ro & EXT4_FEATURE_RO_COMPAT_QUOTA)
193                 ext4_dbg(DEBUG_FS, DBG_NONE "quota\n");
194         if (features_ro & EXT4_FEATURE_RO_COMPAT_BIGALLOC)
195                 ext4_dbg(DEBUG_FS, DBG_NONE "bigalloc\n");
196         if (features_ro & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)
197                 ext4_dbg(DEBUG_FS, DBG_NONE "metadata_csum\n");
198 }
199
200 int ext4_fs_check_features(struct ext4_fs *fs, bool *read_only)
201 {
202         ext4_assert(fs && read_only);
203         uint32_t v;
204         if (ext4_get32(&fs->sb, rev_level) == 0) {
205                 *read_only = false;
206                 return EOK;
207         }
208
209         ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_incompatible:\n");
210         ext4_fs_debug_features_inc(ext4_get32(&fs->sb, features_incompatible));
211
212         ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_compatible:\n");
213         ext4_fs_debug_features_comp(ext4_get32(&fs->sb, features_compatible));
214
215         ext4_dbg(DEBUG_FS, DBG_INFO "sblock features_read_only:\n");
216         ext4_fs_debug_features_ro(ext4_get32(&fs->sb, features_read_only));
217
218         /*Check features_incompatible*/
219         v = (ext4_get32(&fs->sb, features_incompatible) &
220              (~CONFIG_FEATURE_INCOMPAT_SUPP));
221         if (v) {
222                 ext4_dbg(DEBUG_FS, DBG_ERROR
223                                 "sblock has unsupported features incompatible:\n");
224                 ext4_fs_debug_features_inc(v);
225                 return ENOTSUP;
226         }
227
228         /*Check features_read_only*/
229         v = (ext4_get32(&fs->sb, features_read_only) &
230              (~CONFIG_FEATURE_RO_COMPAT_SUPP));
231         if (v) {
232                 ext4_dbg(DEBUG_FS, DBG_WARN
233                                 "sblock has unsupported features read only:\n");
234                 ext4_fs_debug_features_ro(v);
235                 *read_only = true;
236                 return EOK;
237         }
238         *read_only = false;
239
240         return EOK;
241 }
242
243 /**@brief Determine whether the block is inside the group.
244  * @param baddr   block address
245  * @param bgid    block group id
246  * @return Error code
247  */
248 static int ext4_block_in_group(struct ext4_sblock *s,
249                                ext4_fsblk_t baddr,
250                                uint32_t bgid)
251 {
252         uint32_t actual_bgid;
253         actual_bgid = ext4_balloc_get_bgid_of_block(s, baddr);
254         if (actual_bgid == bgid)
255                 return 1;
256         return 0;
257 }
258
259 /**@brief   To avoid calling the atomic setbit hundreds or thousands of times, we only
260  *          need to use it within a single byte (to ensure we get endianness right).
261  *          We can use memset for the rest of the bitmap as there are no other users.
262  */
263 static void ext4_fs_mark_bitmap_end(int start_bit, int end_bit, void *bitmap)
264 {
265         int i;
266
267         if (start_bit >= end_bit)
268                 return;
269
270         for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++)
271                 ext4_bmap_bit_set(bitmap, i);
272
273         if (i < end_bit)
274                 memset((char *)bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
275 }
276
277 /**@brief Initialize block bitmap in block group.
278  * @param bg_ref Reference to block group
279  * @return Error code
280  */
281 static int ext4_fs_init_block_bitmap(struct ext4_block_group_ref *bg_ref)
282 {
283         uint32_t i, bit, bit_max;
284         uint32_t group_blocks;
285         uint16_t inode_size = ext4_get16(&bg_ref->fs->sb, inode_size);
286         uint32_t block_size = ext4_sb_get_block_size(&bg_ref->fs->sb);
287         uint32_t inodes_per_group = ext4_get32(&bg_ref->fs->sb, inodes_per_group);
288         ext4_fsblk_t bitmap_block_addr =
289             ext4_bg_get_block_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
290         ext4_fsblk_t bitmap_inode_addr =
291             ext4_bg_get_inode_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
292         ext4_fsblk_t inode_table_addr =
293             ext4_bg_get_inode_table_first_block(bg_ref->block_group,
294                                                 &bg_ref->fs->sb);
295         ext4_fsblk_t first_group_addr =
296             ext4_balloc_get_block_of_bgid(&bg_ref->fs->sb, bg_ref->index);
297
298         uint32_t dsc_per_block =
299             ext4_sb_get_block_size(&bg_ref->fs->sb) /
300             ext4_sb_get_desc_size(&bg_ref->fs->sb);
301
302         bool flex_bg =
303                 ext4_sb_has_feature_incompatible(&bg_ref->fs->sb,
304                                                  EXT4_FEATURE_INCOMPAT_FLEX_BG);
305
306         uint32_t inode_table_bcnt = inodes_per_group * inode_size / block_size;
307
308         struct ext4_block block_bitmap;
309         int rc =
310             ext4_block_get(bg_ref->fs->bdev, &block_bitmap, bitmap_block_addr);
311         if (rc != EOK)
312                 return rc;
313
314         memset(block_bitmap.data, 0, block_size);
315
316         bit_max = ext4_sb_is_super_in_bg(&bg_ref->fs->sb, bg_ref->index);
317         if (!ext4_sb_has_feature_incompatible(&bg_ref->fs->sb,
318                                               EXT4_FEATURE_INCOMPAT_META_BG) ||
319                         bg_ref->index < ext4_sb_first_meta_bg(&bg_ref->fs->sb) *
320                         dsc_per_block) {
321                 if (bit_max) {
322                         bit_max += ext4_bg_num_gdb(&bg_ref->fs->sb,
323                                                    bg_ref->index);
324                         bit_max +=
325                                 ext4_get16(&bg_ref->fs->sb,
326                                            s_reserved_gdt_blocks);
327                 }
328         } else { /* For META_BG_BLOCK_GROUPS */
329                 bit_max += ext4_bg_num_gdb(&bg_ref->fs->sb,
330                                            bg_ref->index);
331         }
332         for (bit = 0; bit < bit_max; bit++)
333                 ext4_bmap_bit_set(block_bitmap.data, bit);
334
335         if (bg_ref->index == ext4_block_group_cnt(&bg_ref->fs->sb) - 1) {
336                 /*
337                  * Even though mke2fs always initialize first and last group
338                  * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need
339                  * to make sure we calculate the right free blocks
340                  */
341                 group_blocks = (ext4_sb_get_blocks_cnt(&bg_ref->fs->sb) -
342                                 ext4_get32(&bg_ref->fs->sb, first_data_block) -
343                                 (ext4_get32(&bg_ref->fs->sb, blocks_per_group) *
344                                  (ext4_block_group_cnt(&bg_ref->fs->sb) - 1)));
345         } else {
346                 group_blocks = ext4_get32(&bg_ref->fs->sb, blocks_per_group);
347         }
348         if (!flex_bg ||
349             ext4_block_in_group(&bg_ref->fs->sb,
350                                 bitmap_block_addr, bg_ref->index))
351                 ext4_bmap_bit_set(block_bitmap.data,
352                                   bitmap_block_addr - first_group_addr);
353
354         if (!flex_bg ||
355             ext4_block_in_group(&bg_ref->fs->sb,
356                                 bitmap_inode_addr, bg_ref->index))
357                 ext4_bmap_bit_set(block_bitmap.data,
358                                   bitmap_inode_addr - first_group_addr);
359
360         for (i = inode_table_addr;
361                 i < inode_table_addr + inode_table_bcnt; i++) {
362                 if (!flex_bg ||
363                     ext4_block_in_group(&bg_ref->fs->sb,
364                                         i,
365                                         bg_ref->index))
366                         ext4_bmap_bit_set(block_bitmap.data,
367                                         i - first_group_addr);
368         }
369         /*
370          * Also if the number of blocks within the group is
371          * less than the blocksize * 8 ( which is the size
372          * of bitmap ), set rest of the block bitmap to 1
373          */
374         ext4_fs_mark_bitmap_end(group_blocks, block_size * 8, block_bitmap.data);
375         block_bitmap.dirty = true;
376
377         /* Save bitmap */
378         return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
379 }
380
381 /**@brief Initialize i-node bitmap in block group.
382  * @param bg_ref Reference to block group
383  * @return Error code
384  */
385 static int ext4_fs_init_inode_bitmap(struct ext4_block_group_ref *bg_ref)
386 {
387         /* Load bitmap */
388         ext4_fsblk_t bitmap_block_addr =
389             ext4_bg_get_inode_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
390
391         struct ext4_block block_bitmap;
392         int rc =
393             ext4_block_get(bg_ref->fs->bdev, &block_bitmap, bitmap_block_addr);
394         if (rc != EOK)
395                 return rc;
396
397         /* Initialize all bitmap bits to zero */
398         uint32_t block_size = ext4_sb_get_block_size(&bg_ref->fs->sb);
399         uint32_t inodes_per_group =
400             ext4_get32(&bg_ref->fs->sb, inodes_per_group);
401
402         memset(block_bitmap.data, 0, (inodes_per_group + 7) / 8);
403
404         uint32_t start_bit = inodes_per_group;
405         uint32_t end_bit = block_size * 8;
406
407         uint32_t i;
408         for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++)
409                 ext4_bmap_bit_set(block_bitmap.data, i);
410
411         if (i < end_bit)
412                 memset(block_bitmap.data + (i >> 3), 0xff, (end_bit - i) >> 3);
413
414         block_bitmap.dirty = true;
415
416         /* Save bitmap */
417         return ext4_block_set(bg_ref->fs->bdev, &block_bitmap);
418 }
419
420 /**@brief Initialize i-node table in block group.
421  * @param bg_ref Reference to block group
422  * @return Error code
423  */
424 static int ext4_fs_init_inode_table(struct ext4_block_group_ref *bg_ref)
425 {
426         struct ext4_sblock *sb = &bg_ref->fs->sb;
427
428         uint32_t inode_size = ext4_get32(sb, inode_size);
429         uint32_t block_size = ext4_sb_get_block_size(sb);
430         uint32_t inodes_per_block = block_size / inode_size;
431         uint32_t inodes_in_group = ext4_inodes_in_group_cnt(sb, bg_ref->index);
432         uint32_t table_blocks = inodes_in_group / inodes_per_block;
433         ext4_fsblk_t fblock;
434
435         if (inodes_in_group % inodes_per_block)
436                 table_blocks++;
437
438         /* Compute initialization bounds */
439         ext4_fsblk_t first_block =
440             ext4_bg_get_inode_table_first_block(bg_ref->block_group, sb);
441
442         ext4_fsblk_t last_block = first_block + table_blocks - 1;
443
444         /* Initialization of all itable blocks */
445         for (fblock = first_block; fblock <= last_block; ++fblock) {
446
447                 struct ext4_block block;
448                 int rc = ext4_block_get(bg_ref->fs->bdev, &block, fblock);
449                 if (rc != EOK)
450                         return rc;
451
452                 memset(block.data, 0, block_size);
453                 block.dirty = true;
454
455                 ext4_block_set(bg_ref->fs->bdev, &block);
456                 if (rc != EOK)
457                         return rc;
458         }
459
460         return EOK;
461 }
462
463 static ext4_fsblk_t ext4_fs_get_descriptor_block(struct ext4_sblock *s,
464                                              uint32_t bgid,
465                                              uint32_t dsc_per_block)
466 {
467         uint32_t first_meta_bg, dsc_id;
468
469         int has_super = 0;
470
471         dsc_id = bgid / dsc_per_block;
472         first_meta_bg = ext4_sb_first_meta_bg(s);
473
474         if (!ext4_sb_has_feature_incompatible(s,
475                                               EXT4_FEATURE_INCOMPAT_META_BG) ||
476             dsc_id < first_meta_bg)
477                 return ext4_get32(s, first_data_block) + dsc_id + 1;
478
479         if (ext4_sb_is_super_in_bg(s, bgid))
480                 has_super = 1;
481
482         return (has_super + ext4_fs_first_bg_block_no(s, bgid));
483 }
484
485 int ext4_fs_get_block_group_ref(struct ext4_fs *fs, uint32_t bgid,
486                                 struct ext4_block_group_ref *ref)
487 {
488         /* Compute number of descriptors, that fits in one data block */
489         uint32_t dsc_per_block =
490             ext4_sb_get_block_size(&fs->sb) / ext4_sb_get_desc_size(&fs->sb);
491
492         /* Block group descriptor table starts at the next block after
493          * superblock */
494         uint64_t block_id =
495             ext4_fs_get_descriptor_block(&fs->sb, bgid, dsc_per_block);
496
497         uint32_t offset =
498             (bgid % dsc_per_block) * ext4_sb_get_desc_size(&fs->sb);
499
500         int rc = ext4_block_get(fs->bdev, &ref->block, block_id);
501         if (rc != EOK)
502                 return rc;
503
504         ref->block_group = (void *)(ref->block.data + offset);
505         ref->fs = fs;
506         ref->index = bgid;
507         ref->dirty = false;
508
509         if (ext4_bg_has_flag(ref->block_group, EXT4_BLOCK_GROUP_BLOCK_UNINIT)) {
510                 rc = ext4_fs_init_block_bitmap(ref);
511                 if (rc != EOK) {
512                         ext4_block_set(fs->bdev, &ref->block);
513                         return rc;
514                 }
515                 ext4_bg_clear_flag(ref->block_group,
516                                    EXT4_BLOCK_GROUP_BLOCK_UNINIT);
517
518                 ref->dirty = true;
519         }
520
521         if (ext4_bg_has_flag(ref->block_group, EXT4_BLOCK_GROUP_INODE_UNINIT)) {
522                 rc = ext4_fs_init_inode_bitmap(ref);
523                 if (rc != EOK) {
524                         ext4_block_set(ref->fs->bdev, &ref->block);
525                         return rc;
526                 }
527
528                 ext4_bg_clear_flag(ref->block_group,
529                                    EXT4_BLOCK_GROUP_INODE_UNINIT);
530
531                 if (!ext4_bg_has_flag(ref->block_group,
532                                       EXT4_BLOCK_GROUP_ITABLE_ZEROED)) {
533                         rc = ext4_fs_init_inode_table(ref);
534                         if (rc != EOK) {
535                                 ext4_block_set(fs->bdev, &ref->block);
536                                 return rc;
537                         }
538
539                         ext4_bg_set_flag(ref->block_group,
540                                          EXT4_BLOCK_GROUP_ITABLE_ZEROED);
541                 }
542
543                 ref->dirty = true;
544         }
545
546         return EOK;
547 }
548
549 /*
550  * BIG FAT NOTES:
551  *       Currently we do not verify the checksum of block_group_desc
552  *       and inode.
553  */
554
555 /**@brief  Compute checksum of block group descriptor.
556  * @param sb   Superblock
557  * @param bgid Index of block group in the filesystem
558  * @param bg   Block group to compute checksum for
559  * @return Checksum value
560  */
561 static uint16_t ext4_fs_bg_checksum(struct ext4_sblock *sb, uint32_t bgid,
562                                     struct ext4_bgroup *bg)
563 {
564         /* If checksum not supported, 0 will be returned */
565         uint16_t crc = 0;
566
567         /* Compute the checksum only if the filesystem supports it */
568         if (ext4_sb_has_feature_read_only(sb,
569                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
570                 /* Use metadata_csum algorithm instead */
571                 uint32_t le32_bgid = to_le32(bgid);
572                 uint32_t orig_checksum, checksum;
573
574                 /* Preparation: temporarily set bg checksum to 0 */
575                 orig_checksum = bg->checksum;
576                 bg->checksum = 0;
577
578                 /* First calculate crc32 checksum against fs uuid */
579                 checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
580                 /* Then calculate crc32 checksum against bgid */
581                 checksum = ext4_crc32c(checksum, &le32_bgid,
582                                      sizeof(bgid));
583                 /* Finally calculate crc32 checksum against block_group_desc */
584                 checksum = ext4_crc32c(checksum, bg,
585                                      ext4_sb_get_desc_size(sb));
586                 bg->checksum = orig_checksum;
587
588                 crc = checksum & 0xFFFF;
589         } else if (ext4_sb_has_feature_read_only(sb,
590                                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
591                 uint8_t *base = (uint8_t *)bg;
592                 uint8_t *checksum = (uint8_t *)&bg->checksum;
593
594                 uint32_t offset = (uint32_t)(checksum - base);
595
596                 /* Convert block group index to little endian */
597                 uint32_t le_group = to_le32(bgid);
598
599                 /* Initialization */
600                 crc = ext4_bg_crc16(~0, sb->uuid, sizeof(sb->uuid));
601
602                 /* Include index of block group */
603                 crc =
604                     ext4_bg_crc16(crc, (uint8_t *)&le_group, sizeof(le_group));
605
606                 /* Compute crc from the first part (stop before checksum field)
607                  */
608                 crc = ext4_bg_crc16(crc, (uint8_t *)bg, offset);
609
610                 /* Skip checksum */
611                 offset += sizeof(bg->checksum);
612
613                 /* Checksum of the rest of block group descriptor */
614                 if ((ext4_sb_has_feature_incompatible(
615                         sb, EXT4_FEATURE_INCOMPAT_64BIT)) &&
616                     (offset < ext4_sb_get_desc_size(sb)))
617
618                         crc = ext4_bg_crc16(crc, ((uint8_t *)bg) + offset,
619                                             ext4_sb_get_desc_size(sb) - offset);
620         }
621         return crc;
622 }
623
624 int ext4_fs_put_block_group_ref(struct ext4_block_group_ref *ref)
625 {
626         /* Check if reference modified */
627         if (ref->dirty) {
628                 /* Compute new checksum of block group */
629                 uint16_t checksum = ext4_fs_bg_checksum(
630                     &ref->fs->sb, ref->index, ref->block_group);
631
632                 ref->block_group->checksum = to_le16(checksum);
633
634                 /* Mark block dirty for writing changes to physical device */
635                 ref->block.dirty = true;
636         }
637
638         /* Put back block, that contains block group descriptor */
639         return ext4_block_set(ref->fs->bdev, &ref->block);
640 }
641
642 static uint32_t ext4_fs_inode_checksum(struct ext4_inode_ref *inode_ref)
643 {
644         uint32_t checksum = 0;
645         struct ext4_sblock *sb = &inode_ref->fs->sb;
646         uint16_t inode_size = ext4_get16(sb, inode_size);
647
648         if (ext4_sb_has_feature_read_only(sb,
649                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
650                 uint32_t orig_checksum;
651
652                 uint32_t ino_index = to_le32(inode_ref->index);
653                 uint32_t ino_gen =
654                         to_le32(ext4_inode_get_generation(inode_ref->inode));
655
656                 /* Preparation: temporarily set bg checksum to 0 */
657                 orig_checksum = ext4_inode_get_checksum(sb, inode_ref->inode);
658                 ext4_inode_set_checksum(sb, inode_ref->inode, 0);
659
660                 /* First calculate crc32 checksum against fs uuid */
661                 checksum = ext4_crc32c(~0, sb->uuid, sizeof(sb->uuid));
662                 /* Then calculate crc32 checksum against inode number
663                  * and inode generation */
664                 checksum = ext4_crc32c(checksum, &ino_index,
665                                      sizeof(ino_index));
666                 checksum = ext4_crc32c(checksum, &ino_gen,
667                                      sizeof(ino_gen));
668                 /* Finally calculate crc32 checksum against 
669                  * the entire inode */
670                 checksum = ext4_crc32c(checksum, inode_ref->inode,
671                                 inode_size);
672                 ext4_inode_set_checksum(sb, inode_ref->inode,
673                                 orig_checksum);
674         }
675         return checksum;
676 }
677
678 static void ext4_fs_set_inode_checksum(struct ext4_inode_ref *inode_ref)
679 {
680         struct ext4_sblock *sb = &inode_ref->fs->sb;
681         if (!ext4_sb_has_feature_read_only(sb,
682                                 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
683                 return;
684
685         ext4_inode_set_checksum(sb, inode_ref->inode,
686                                 ext4_fs_inode_checksum(inode_ref));
687 }
688
689 int ext4_fs_get_inode_ref(struct ext4_fs *fs, uint32_t index,
690                           struct ext4_inode_ref *ref)
691 {
692         /* Compute number of i-nodes, that fits in one data block */
693         uint32_t inodes_per_group = ext4_get32(&fs->sb, inodes_per_group);
694
695         /*
696          * Inode numbers are 1-based, but it is simpler to work with 0-based
697          * when computing indices
698          */
699         index -= 1;
700         uint32_t block_group = index / inodes_per_group;
701         uint32_t offset_in_group = index % inodes_per_group;
702
703         /* Load block group, where i-node is located */
704         struct ext4_block_group_ref bg_ref;
705
706         int rc = ext4_fs_get_block_group_ref(fs, block_group, &bg_ref);
707         if (rc != EOK) {
708                 return rc;
709         }
710
711         /* Load block address, where i-node table is located */
712         uint32_t inode_table_start =
713             ext4_bg_get_inode_table_first_block(bg_ref.block_group, &fs->sb);
714
715         /* Put back block group reference (not needed more) */
716         rc = ext4_fs_put_block_group_ref(&bg_ref);
717         if (rc != EOK) {
718                 return rc;
719         }
720
721         /* Compute position of i-node in the block group */
722         uint16_t inode_size = ext4_get16(&fs->sb, inode_size);
723         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
724         uint32_t byte_offset_in_group = offset_in_group * inode_size;
725
726         /* Compute block address */
727         ext4_fsblk_t block_id =
728             inode_table_start + (byte_offset_in_group / block_size);
729
730         rc = ext4_block_get(fs->bdev, &ref->block, block_id);
731         if (rc != EOK) {
732                 return rc;
733         }
734
735         /* Compute position of i-node in the data block */
736         uint32_t offset_in_block = byte_offset_in_group % block_size;
737         ref->inode = (struct ext4_inode *)(ref->block.data + offset_in_block);
738
739         /* We need to store the original value of index in the reference */
740         ref->index = index + 1;
741         ref->fs = fs;
742         ref->dirty = false;
743
744         return EOK;
745 }
746
747 int ext4_fs_put_inode_ref(struct ext4_inode_ref *ref)
748 {
749         /* Check if reference modified */
750         if (ref->dirty) {
751                 /* Mark block dirty for writing changes to physical device */
752                 ext4_fs_set_inode_checksum(ref);
753                 ref->block.dirty = true;
754         }
755
756         /* Put back block, that contains i-node */
757         return ext4_block_set(ref->fs->bdev, &ref->block);
758 }
759
760 void ext4_fs_inode_blocks_init(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref)
761 {
762         int i;
763         struct ext4_inode *inode = inode_ref->inode;
764
765         for (i = 0; i < EXT4_INODE_BLOCKS; i++)
766                 inode->blocks[i] = 0;
767
768         (void)fs;
769 #if CONFIG_EXTENT_ENABLE
770         /* Initialize extents if needed */
771         if (ext4_sb_has_feature_incompatible(&fs->sb,
772                                 EXT4_FEATURE_INCOMPAT_EXTENTS)) {
773                 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
774
775                 /* Initialize extent root header */
776                 ext4_extent_tree_init(inode_ref);
777         }
778 #endif
779 }
780
781 static uint32_t ext4_fs_correspond_inode_mode(int filetype)
782 {
783         switch (filetype) {
784         case EXT4_DIRENTRY_DIR:
785                 return EXT4_INODE_MODE_DIRECTORY;
786         case EXT4_DIRENTRY_REG_FILE:
787                 return EXT4_INODE_MODE_FILE;
788         case EXT4_DIRENTRY_SYMLINK:
789                 return EXT4_INODE_MODE_SOFTLINK;
790         default:
791                 /* FIXME: right now we only support 3 file type. */
792                 ext4_assert(0);
793         }
794         return 0;
795 }
796
797 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
798                         int filetype)
799 {
800         /* Check if newly allocated i-node will be a directory */
801         bool is_dir;
802
803         is_dir = (filetype == EXT4_DIRENTRY_DIR);
804
805         /* Allocate inode by allocation algorithm */
806         uint32_t index;
807         int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
808         if (rc != EOK)
809                 return rc;
810
811         /* Load i-node from on-disk i-node table */
812         rc = ext4_fs_get_inode_ref(fs, index, inode_ref);
813         if (rc != EOK) {
814                 ext4_ialloc_free_inode(fs, index, is_dir);
815                 return rc;
816         }
817
818         /* Initialize i-node */
819         struct ext4_inode *inode = inode_ref->inode;
820
821         uint32_t mode;
822         if (is_dir) {
823                 /*
824                  * Default directory permissions to be compatible with other
825                  * systems
826                  * 0777 (octal) == rwxrwxrwx
827                  */
828
829                 mode = 0777;
830                 mode |= EXT4_INODE_MODE_DIRECTORY;
831         } else {
832                 /*
833                  * Default file permissions to be compatible with other systems
834                  * 0666 (octal) == rw-rw-rw-
835                  */
836
837                 mode = 0666;
838                 mode |= ext4_fs_correspond_inode_mode(filetype);
839         }
840         ext4_inode_set_mode(&fs->sb, inode, mode);
841
842         ext4_inode_set_links_count(inode, 0);
843         ext4_inode_set_uid(inode, 0);
844         ext4_inode_set_gid(inode, 0);
845         ext4_inode_set_size(inode, 0);
846         ext4_inode_set_access_time(inode, 0);
847         ext4_inode_set_change_inode_time(inode, 0);
848         ext4_inode_set_modification_time(inode, 0);
849         ext4_inode_set_deletion_time(inode, 0);
850         ext4_inode_set_blocks_count(&fs->sb, inode, 0);
851         ext4_inode_set_flags(inode, 0);
852         ext4_inode_set_generation(inode, 0);
853
854         /* Reset blocks array. For symbolic link inode, just
855          * fill in blocks with 0 */
856         if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
857                 for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
858                         inode->blocks[i] = 0;
859
860         } else
861                 ext4_fs_inode_blocks_init(fs, inode_ref);
862
863         inode_ref->dirty = true;
864
865         return EOK;
866 }
867
868 int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
869 {
870         struct ext4_fs *fs = inode_ref->fs;
871         uint32_t offset;
872         uint32_t suboff;
873         int rc;
874 #if CONFIG_EXTENT_ENABLE
875         /* For extents must be data block destroyed by other way */
876         if ((ext4_sb_has_feature_incompatible(&fs->sb,
877                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
878             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
879                 /* Data structures are released during truncate operation... */
880                 goto finish;
881         }
882 #endif
883         /* Release all indirect (no data) blocks */
884
885         /* 1) Single indirect */
886         ext4_fsblk_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
887         if (fblock != 0) {
888                 int rc = ext4_balloc_free_block(inode_ref, fblock);
889                 if (rc != EOK)
890                         return rc;
891
892                 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
893         }
894
895         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
896         uint32_t count = block_size / sizeof(uint32_t);
897
898         struct ext4_block block;
899
900         /* 2) Double indirect */
901         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
902         if (fblock != 0) {
903                 int rc = ext4_block_get(fs->bdev, &block, fblock);
904                 if (rc != EOK)
905                         return rc;
906
907                 ext4_fsblk_t ind_block;
908                 for (offset = 0; offset < count; ++offset) {
909                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
910
911                         if (ind_block == 0)
912                                 continue;
913                         rc = ext4_balloc_free_block(inode_ref, ind_block);
914                         if (rc != EOK) {
915                                 ext4_block_set(fs->bdev, &block);
916                                 return rc;
917                         }
918
919                 }
920
921                 ext4_block_set(fs->bdev, &block);
922                 rc = ext4_balloc_free_block(inode_ref, fblock);
923                 if (rc != EOK)
924                         return rc;
925
926                 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
927         }
928
929         /* 3) Tripple indirect */
930         struct ext4_block subblock;
931         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
932         if (fblock == 0)
933                 goto finish;
934         rc = ext4_block_get(fs->bdev, &block, fblock);
935         if (rc != EOK)
936                 return rc;
937
938         ext4_fsblk_t ind_block;
939         for (offset = 0; offset < count; ++offset) {
940                 ind_block = to_le32(((uint32_t *)block.data)[offset]);
941
942                 if (ind_block == 0)
943                         continue;
944                 rc = ext4_block_get(fs->bdev, &subblock,
945                                 ind_block);
946                 if (rc != EOK) {
947                         ext4_block_set(fs->bdev, &block);
948                         return rc;
949                 }
950
951                 ext4_fsblk_t ind_subblk;
952                 for (suboff = 0; suboff < count; ++suboff) {
953                         ind_subblk = to_le32(((uint32_t *)subblock.data)[suboff]);
954
955                         if (ind_subblk == 0)
956                                 continue;
957                         rc = ext4_balloc_free_block(inode_ref, ind_subblk);
958                         if (rc != EOK) {
959                                 ext4_block_set(fs->bdev, &subblock);
960                                 ext4_block_set(fs->bdev, &block);
961                                 return rc;
962                         }
963
964                 }
965
966                 ext4_block_set(fs->bdev, &subblock);
967
968                 rc = ext4_balloc_free_block(inode_ref,
969                                 ind_block);
970                 if (rc != EOK) {
971                         ext4_block_set(fs->bdev, &block);
972                         return rc;
973                 }
974
975         }
976
977         ext4_block_set(fs->bdev, &block);
978         rc = ext4_balloc_free_block(inode_ref, fblock);
979         if (rc != EOK)
980                 return rc;
981
982         ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
983 finish:
984         /* Mark inode dirty for writing to the physical device */
985         inode_ref->dirty = true;
986
987         /* Free block with extended attributes if present */
988         ext4_fsblk_t xattr_block =
989             ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
990         if (xattr_block) {
991                 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
992                 if (rc != EOK)
993                         return rc;
994
995                 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
996         }
997
998         /* Free inode by allocator */
999         if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
1000                                EXT4_INODE_MODE_DIRECTORY))
1001                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
1002         else
1003                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
1004
1005         return rc;
1006 }
1007
1008
1009 /**@brief Release data block from i-node
1010  * @param inode_ref I-node to release block from
1011  * @param iblock    Logical block to be released
1012  * @return Error code
1013  */
1014 static int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
1015                                 uint32_t iblock)
1016 {
1017         ext4_fsblk_t fblock;
1018
1019         struct ext4_fs *fs = inode_ref->fs;
1020
1021         /* Extents are handled otherwise = there is not support in this function
1022          */
1023         ext4_assert(!(
1024             ext4_sb_has_feature_incompatible(&fs->sb,
1025                                              EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1026             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1027
1028         struct ext4_inode *inode = inode_ref->inode;
1029
1030         /* Handle simple case when we are dealing with direct reference */
1031         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1032                 fblock = ext4_inode_get_direct_block(inode, iblock);
1033
1034                 /* Sparse file */
1035                 if (fblock == 0)
1036                         return EOK;
1037
1038                 ext4_inode_set_direct_block(inode, iblock, 0);
1039                 return ext4_balloc_free_block(inode_ref, fblock);
1040         }
1041
1042         /* Determine the indirection level needed to get the desired block */
1043         unsigned int level = 0;
1044         unsigned int i;
1045         for (i = 1; i < 4; i++) {
1046                 if (iblock < fs->inode_block_limits[i]) {
1047                         level = i;
1048                         break;
1049                 }
1050         }
1051
1052         if (level == 0)
1053                 return EIO;
1054
1055         /* Compute offsets for the topmost level */
1056         uint64_t block_offset_in_level =
1057             iblock - fs->inode_block_limits[level - 1];
1058         ext4_fsblk_t current_block =
1059             ext4_inode_get_indirect_block(inode, level - 1);
1060         uint32_t offset_in_block =
1061             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1062
1063         /*
1064          * Navigate through other levels, until we find the block number
1065          * or find null reference meaning we are dealing with sparse file
1066          */
1067         struct ext4_block block;
1068
1069         while (level > 0) {
1070
1071                 /* Sparse check */
1072                 if (current_block == 0)
1073                         return EOK;
1074
1075                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1076                 if (rc != EOK)
1077                         return rc;
1078
1079                 current_block =
1080                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1081
1082                 /* Set zero if physical data block address found */
1083                 if (level == 1) {
1084                         ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
1085                         block.dirty = true;
1086                 }
1087
1088                 rc = ext4_block_set(fs->bdev, &block);
1089                 if (rc != EOK)
1090                         return rc;
1091
1092                 level--;
1093
1094                 /*
1095                  * If we are on the last level, break here as
1096                  * there is no next level to visit
1097                  */
1098                 if (level == 0)
1099                         break;
1100
1101                 /* Visit the next level */
1102                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1103                 offset_in_block = block_offset_in_level /
1104                                   fs->inode_blocks_per_level[level - 1];
1105         }
1106
1107         fblock = current_block;
1108         if (fblock == 0)
1109                 return EOK;
1110
1111         /* Physical block is not referenced, it can be released */
1112         return ext4_balloc_free_block(inode_ref, fblock);
1113 }
1114
1115 int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
1116 {
1117         struct ext4_sblock *sb = &inode_ref->fs->sb;
1118         uint32_t i;
1119
1120         /* Check flags, if i-node can be truncated */
1121         if (!ext4_inode_can_truncate(sb, inode_ref->inode))
1122                 return EINVAL;
1123
1124         /* If sizes are equal, nothing has to be done. */
1125         uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
1126         if (old_size == new_size)
1127                 return EOK;
1128
1129         /* It's not supported to make the larger file by truncate operation */
1130         if (old_size < new_size)
1131                 return EINVAL;
1132
1133         if (ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK)
1134                         && old_size < sizeof(inode_ref->inode->blocks)
1135                         && !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
1136                 char *content = (char *)inode_ref->inode->blocks;
1137                 memset(content + new_size, 0,
1138                         sizeof(inode_ref->inode->blocks) - new_size);
1139                 ext4_inode_set_size(inode_ref->inode, new_size);
1140                 inode_ref->dirty = true;
1141
1142                 return EOK;
1143         }
1144
1145         /* Compute how many blocks will be released */
1146         uint32_t block_size = ext4_sb_get_block_size(sb);
1147         uint32_t new_blocks_count = (new_size + block_size - 1) /
1148                                     block_size;
1149         uint32_t old_blocks_count = (old_size + block_size - 1) /
1150                                     block_size;
1151         uint32_t diff_blocks_count = old_blocks_count - new_blocks_count;
1152 #if CONFIG_EXTENT_ENABLE
1153         if ((ext4_sb_has_feature_incompatible(sb,
1154                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1155             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1156
1157                 /* Extents require special operation */
1158                 if (diff_blocks_count) {
1159                         int rc = ext4_extent_remove_space(inode_ref,
1160                                         new_blocks_count, EXT_MAX_BLOCKS);
1161                         if (rc != EOK)
1162                                 return rc;
1163
1164                 }
1165         } else
1166 #endif
1167         {
1168                 /* Release data blocks from the end of file */
1169
1170                 /* Starting from 1 because of logical blocks are numbered from 0
1171                  */
1172                 for (i = 0; i < diff_blocks_count; ++i) {
1173                         int rc = ext4_fs_release_inode_block(
1174                             inode_ref, new_blocks_count + i);
1175                         if (rc != EOK)
1176                                 return rc;
1177                 }
1178         }
1179
1180         /* Update i-node */
1181         ext4_inode_set_size(inode_ref->inode, new_size);
1182         inode_ref->dirty = true;
1183
1184         return EOK;
1185 }
1186
1187 /**@brief Compute 'goal' for inode index
1188  * @param inode_ref Reference to inode, to allocate block for
1189  * @return goal
1190  */
1191 ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref)
1192 {
1193         uint32_t group_inodes =
1194                 ext4_get32(&inode_ref->fs->sb, inodes_per_group);
1195         return (inode_ref->index - 1) / group_inodes;
1196 }
1197
1198 /**@brief Compute 'goal' for allocation algorithm (For blockmap).
1199  * @param inode_ref Reference to inode, to allocate block for
1200  * @param goal
1201  * @return error code
1202  */
1203 int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
1204                                 ext4_fsblk_t *goal)
1205 {
1206         struct ext4_sblock *sb = &inode_ref->fs->sb;
1207         *goal = 0;
1208
1209         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1210         uint32_t block_size = ext4_sb_get_block_size(sb);
1211         uint32_t inode_block_count = inode_size / block_size;
1212
1213         if (inode_size % block_size != 0)
1214                 inode_block_count++;
1215
1216         /* If inode has some blocks, get last block address + 1 */
1217         if (inode_block_count > 0) {
1218                 int rc = ext4_fs_get_inode_data_block_index(
1219                     inode_ref, inode_block_count - 1, goal, false);
1220                 if (rc != EOK)
1221                         return rc;
1222
1223                 if (*goal != 0) {
1224                         (*goal)++;
1225                         return rc;
1226                 }
1227
1228                 /* If goal == 0, sparse file -> continue */
1229         }
1230
1231         /* Identify block group of inode */
1232
1233         uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
1234         uint32_t block_group = (inode_ref->index - 1) / inodes_per_group;
1235         block_size = ext4_sb_get_block_size(sb);
1236
1237         /* Load block group reference */
1238         struct ext4_block_group_ref bg_ref;
1239         int rc =
1240             ext4_fs_get_block_group_ref(inode_ref->fs, block_group, &bg_ref);
1241         if (rc != EOK)
1242                 return rc;
1243
1244         /* Compute indexes */
1245         uint32_t block_group_count = ext4_block_group_cnt(sb);
1246         ext4_fsblk_t inode_table_first_block =
1247             ext4_bg_get_inode_table_first_block(bg_ref.block_group, sb);
1248         uint16_t inode_table_item_size = ext4_get16(sb, inode_size);
1249         uint32_t inode_table_bytes;
1250
1251         /* Check for last block group */
1252         if (block_group < block_group_count - 1) {
1253                 inode_table_bytes = inodes_per_group * inode_table_item_size;
1254         } else {
1255                 /* Last block group could be smaller */
1256                 uint32_t inodes_count_total = ext4_get32(sb, inodes_count);
1257
1258                 inode_table_bytes =
1259                     (inodes_count_total -
1260                      ((block_group_count - 1) * inodes_per_group)) *
1261                     inode_table_item_size;
1262         }
1263
1264         ext4_fsblk_t inode_table_blocks = inode_table_bytes / block_size;
1265
1266         if (inode_table_bytes % block_size)
1267                 inode_table_blocks++;
1268
1269         *goal = inode_table_first_block + inode_table_blocks;
1270
1271         return ext4_fs_put_block_group_ref(&bg_ref);
1272 }
1273
1274 static int ext4_fs_get_inode_data_block_idx(struct ext4_inode_ref *inode_ref,
1275                                        uint64_t iblock, ext4_fsblk_t *fblock,
1276                                        bool extent_create,
1277                                        bool support_unwritten)
1278 {
1279         struct ext4_fs *fs = inode_ref->fs;
1280
1281         /* For empty file is situation simple */
1282         if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
1283                 *fblock = 0;
1284                 return EOK;
1285         }
1286
1287         ext4_fsblk_t current_block;
1288
1289         (void)extent_create;
1290 #if CONFIG_EXTENT_ENABLE
1291         /* Handle i-node using extents */
1292         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1293                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1294             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1295
1296                 ext4_fsblk_t current_fsblk;
1297                 int rc = ext4_extent_get_blocks(inode_ref, iblock, 1,
1298                                 &current_fsblk, extent_create, NULL);
1299                 if (rc != EOK)
1300                         return rc;
1301
1302                 current_block = current_fsblk;
1303                 *fblock = current_block;
1304
1305                 ext4_assert(*fblock || support_unwritten);
1306                 return EOK;
1307         }
1308 #endif
1309
1310         struct ext4_inode *inode = inode_ref->inode;
1311
1312         /* Direct block are read directly from array in i-node structure */
1313         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1314                 current_block =
1315                     ext4_inode_get_direct_block(inode, (uint32_t)iblock);
1316                 *fblock = current_block;
1317                 return EOK;
1318         }
1319
1320         /* Determine indirection level of the target block */
1321         unsigned int level = 0;
1322         unsigned int i;
1323         for (i = 1; i < 4; i++) {
1324                 if (iblock < fs->inode_block_limits[i]) {
1325                         level = i;
1326                         break;
1327                 }
1328         }
1329
1330         if (level == 0)
1331                 return EIO;
1332
1333         /* Compute offsets for the topmost level */
1334         uint64_t block_offset_in_level =
1335             iblock - fs->inode_block_limits[level - 1];
1336         current_block = ext4_inode_get_indirect_block(inode, level - 1);
1337         uint32_t offset_in_block =
1338             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1339
1340         /* Sparse file */
1341         if (current_block == 0) {
1342                 *fblock = 0;
1343                 return EOK;
1344         }
1345
1346         struct ext4_block block;
1347
1348         /*
1349          * Navigate through other levels, until we find the block number
1350          * or find null reference meaning we are dealing with sparse file
1351          */
1352         while (level > 0) {
1353                 /* Load indirect block */
1354                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1355                 if (rc != EOK)
1356                         return rc;
1357
1358                 /* Read block address from indirect block */
1359                 current_block =
1360                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1361
1362                 /* Put back indirect block untouched */
1363                 rc = ext4_block_set(fs->bdev, &block);
1364                 if (rc != EOK)
1365                         return rc;
1366
1367                 /* Check for sparse file */
1368                 if (current_block == 0) {
1369                         *fblock = 0;
1370                         return EOK;
1371                 }
1372
1373                 /* Jump to the next level */
1374                 level--;
1375
1376                 /* Termination condition - we have address of data block loaded
1377                  */
1378                 if (level == 0)
1379                         break;
1380
1381                 /* Visit the next level */
1382                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1383                 offset_in_block = block_offset_in_level /
1384                                   fs->inode_blocks_per_level[level - 1];
1385         }
1386
1387         *fblock = current_block;
1388
1389         return EOK;
1390 }
1391
1392
1393 int ext4_fs_get_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1394                                        uint64_t iblock, ext4_fsblk_t *fblock,
1395                                        bool support_unwritten)
1396 {
1397         return ext4_fs_get_inode_data_block_idx(inode_ref, iblock, fblock,
1398                         false, support_unwritten);
1399 }
1400
1401 int ext4_fs_init_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1402                                        uint64_t iblock, ext4_fsblk_t *fblock)
1403 {
1404         return ext4_fs_get_inode_data_block_idx(inode_ref, iblock, fblock,
1405                         true, true);
1406 }
1407
1408 static int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1409                                        uint64_t iblock, ext4_fsblk_t fblock)
1410 {
1411         struct ext4_fs *fs = inode_ref->fs;
1412
1413 #if CONFIG_EXTENT_ENABLE
1414         /* Handle inode using extents */
1415         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1416                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1417             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1418                 /* Not reachable */
1419                 return ENOTSUP;
1420         }
1421 #endif
1422
1423         /* Handle simple case when we are dealing with direct reference */
1424         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1425                 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
1426                                             (uint32_t)fblock);
1427                 inode_ref->dirty = true;
1428
1429                 return EOK;
1430         }
1431
1432         /* Determine the indirection level needed to get the desired block */
1433         unsigned int level = 0;
1434         unsigned int i;
1435         for (i = 1; i < 4; i++) {
1436                 if (iblock < fs->inode_block_limits[i]) {
1437                         level = i;
1438                         break;
1439                 }
1440         }
1441
1442         if (level == 0)
1443                 return EIO;
1444
1445         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
1446
1447         /* Compute offsets for the topmost level */
1448         uint64_t block_offset_in_level =
1449             iblock - fs->inode_block_limits[level - 1];
1450         ext4_fsblk_t current_block =
1451             ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1452         uint32_t offset_in_block =
1453             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1454
1455         ext4_fsblk_t new_block_addr;
1456
1457         struct ext4_block block;
1458         struct ext4_block new_block;
1459
1460         /* Is needed to allocate indirect block on the i-node level */
1461         if (current_block == 0) {
1462                 /* Allocate new indirect block */
1463                 ext4_fsblk_t goal;
1464                 int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1465                 if (rc != EOK)
1466                         return rc;
1467
1468                 rc = ext4_balloc_alloc_block(inode_ref,
1469                                              goal,
1470                                              &new_block_addr);
1471                 if (rc != EOK)
1472                         return rc;
1473
1474                 /* Update i-node */
1475                 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1476                                               (uint32_t)new_block_addr);
1477                 inode_ref->dirty = true;
1478
1479                 /* Load newly allocated block */
1480                 rc = ext4_block_get(fs->bdev, &new_block, new_block_addr);
1481                 if (rc != EOK) {
1482                         ext4_balloc_free_block(inode_ref, new_block_addr);
1483                         return rc;
1484                 }
1485
1486                 /* Initialize new block */
1487                 memset(new_block.data, 0, block_size);
1488                 new_block.dirty = true;
1489
1490                 /* Put back the allocated block */
1491                 rc = ext4_block_set(fs->bdev, &new_block);
1492                 if (rc != EOK)
1493                         return rc;
1494
1495                 current_block = new_block_addr;
1496         }
1497
1498         /*
1499          * Navigate through other levels, until we find the block number
1500          * or find null reference meaning we are dealing with sparse file
1501          */
1502         while (level > 0) {
1503                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1504                 if (rc != EOK)
1505                         return rc;
1506
1507                 current_block =
1508                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1509
1510                 if ((level > 1) && (current_block == 0)) {
1511                         ext4_fsblk_t goal;
1512                         rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1513                         if (rc != EOK) {
1514                                 ext4_block_set(fs->bdev, &block);
1515                                 return rc;
1516                         }
1517
1518                         /* Allocate new block */
1519                         rc =
1520                             ext4_balloc_alloc_block(inode_ref, goal, &new_block_addr);
1521                         if (rc != EOK) {
1522                                 ext4_block_set(fs->bdev, &block);
1523                                 return rc;
1524                         }
1525
1526                         /* Load newly allocated block */
1527                         rc = ext4_block_get(fs->bdev, &new_block,
1528                                             new_block_addr);
1529
1530                         if (rc != EOK) {
1531                                 ext4_block_set(fs->bdev, &block);
1532                                 return rc;
1533                         }
1534
1535                         /* Initialize allocated block */
1536                         memset(new_block.data, 0, block_size);
1537                         new_block.dirty = true;
1538
1539                         rc = ext4_block_set(fs->bdev, &new_block);
1540                         if (rc != EOK) {
1541                                 ext4_block_set(fs->bdev, &block);
1542                                 return rc;
1543                         }
1544
1545                         /* Write block address to the parent */
1546                         ((uint32_t *)block.data)[offset_in_block] =
1547                             to_le32((uint32_t)new_block_addr);
1548                         block.dirty = true;
1549                         current_block = new_block_addr;
1550                 }
1551
1552                 /* Will be finished, write the fblock address */
1553                 if (level == 1) {
1554                         ((uint32_t *)block.data)[offset_in_block] =
1555                             to_le32((uint32_t)fblock);
1556                         block.dirty = true;
1557                 }
1558
1559                 rc = ext4_block_set(fs->bdev, &block);
1560                 if (rc != EOK)
1561                         return rc;
1562
1563                 level--;
1564
1565                 /*
1566                  * If we are on the last level, break here as
1567                  * there is no next level to visit
1568                  */
1569                 if (level == 0)
1570                         break;
1571
1572                 /* Visit the next level */
1573                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1574                 offset_in_block = block_offset_in_level /
1575                                   fs->inode_blocks_per_level[level - 1];
1576         }
1577
1578         return EOK;
1579 }
1580
1581
1582 int ext4_fs_append_inode_block(struct ext4_inode_ref *inode_ref,
1583                                ext4_fsblk_t *fblock, uint32_t *iblock)
1584 {
1585 #if CONFIG_EXTENT_ENABLE
1586         /* Handle extents separately */
1587         if ((ext4_sb_has_feature_incompatible(&inode_ref->fs->sb,
1588                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1589             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1590                 int rc;
1591                 ext4_fsblk_t current_fsblk;
1592                 struct ext4_sblock *sb = &inode_ref->fs->sb;
1593                 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1594                 uint32_t block_size = ext4_sb_get_block_size(sb);
1595                 *iblock = (inode_size + block_size - 1) /
1596                                     block_size;
1597
1598                 rc = ext4_extent_get_blocks(inode_ref, *iblock, 1,
1599                                 &current_fsblk, true, NULL);
1600
1601
1602                 *fblock = current_fsblk;
1603                 ext4_assert(*fblock);
1604
1605                 ext4_inode_set_size(inode_ref->inode,
1606                                     inode_size + block_size);
1607                 inode_ref->dirty = true;
1608
1609
1610                 return rc;
1611         }
1612 #endif
1613         struct ext4_sblock *sb = &inode_ref->fs->sb;
1614
1615         /* Compute next block index and allocate data block */
1616         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1617         uint32_t block_size = ext4_sb_get_block_size(sb);
1618
1619         /* Align size i-node size */
1620         if ((inode_size % block_size) != 0)
1621                 inode_size += block_size - (inode_size % block_size);
1622
1623         /* Logical blocks are numbered from 0 */
1624         uint32_t new_block_idx = inode_size / block_size;
1625
1626         /* Allocate new physical block */
1627         ext4_fsblk_t goal, phys_block;
1628         int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1629         if (rc != EOK)
1630                 return rc;
1631
1632         rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
1633         if (rc != EOK)
1634                 return rc;
1635
1636         /* Add physical block address to the i-node */
1637         rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
1638                                                 phys_block);
1639         if (rc != EOK) {
1640                 ext4_balloc_free_block(inode_ref, phys_block);
1641                 return rc;
1642         }
1643
1644         /* Update i-node */
1645         ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1646         inode_ref->dirty = true;
1647
1648         *fblock = phys_block;
1649         *iblock = new_block_idx;
1650
1651         return EOK;
1652 }
1653
1654 void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
1655 {
1656         uint16_t link;
1657
1658         link = ext4_inode_get_links_count(inode_ref->inode);
1659         link++;
1660         ext4_inode_set_links_count(inode_ref->inode, link);
1661
1662         bool is_dx =
1663             ext4_sb_has_feature_compatible(&inode_ref->fs->sb,
1664                                            EXT4_FEATURE_COMPAT_DIR_INDEX) &&
1665             ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
1666
1667         if (is_dx && link > 1) {
1668                 if (link >= EXT4_LINK_MAX || link == 2) {
1669                         ext4_inode_set_links_count(inode_ref->inode, 1);
1670
1671                         uint32_t v =
1672                             ext4_get32(&inode_ref->fs->sb, features_read_only);
1673                         v |= EXT4_FEATURE_RO_COMPAT_DIR_NLINK;
1674                         ext4_set32(&inode_ref->fs->sb, features_read_only, v);
1675                 }
1676         }
1677 }
1678
1679 void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
1680 {
1681         uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
1682         if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
1683                                 EXT4_INODE_MODE_DIRECTORY)) {
1684                 if (links > 0)
1685                         ext4_inode_set_links_count(inode_ref->inode, links - 1);
1686                 return;
1687         }
1688
1689         if (links > 2)
1690                 ext4_inode_set_links_count(inode_ref->inode, links - 1);
1691 }
1692
1693 /**
1694  * @}
1695  */