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