Do not set AGGRESSIVE_TEST on.
[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                                ext4_fsblk_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         ext4_fsblk_t bitmap_block_addr =
288             ext4_bg_get_block_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
289         ext4_fsblk_t bitmap_inode_addr =
290             ext4_bg_get_inode_bitmap(bg_ref->block_group, &bg_ref->fs->sb);
291         ext4_fsblk_t inode_table_addr =
292             ext4_bg_get_inode_table_first_block(bg_ref->block_group,
293                                                 &bg_ref->fs->sb);
294         ext4_fsblk_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         ext4_fsblk_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         ext4_fsblk_t fblock;
433
434         if (inodes_in_group % inodes_per_block)
435                 table_blocks++;
436
437         /* Compute initialization bounds */
438         ext4_fsblk_t first_block =
439             ext4_bg_get_inode_table_first_block(bg_ref->block_group, sb);
440
441         ext4_fsblk_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 ext4_fsblk_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         ext4_fsblk_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         (void)fs;
693 #if CONFIG_EXTENT_ENABLE
694         /* Initialize extents if needed */
695         if (ext4_sb_has_feature_incompatible(&fs->sb,
696                                 EXT4_FEATURE_INCOMPAT_EXTENTS)) {
697                 ext4_inode_set_flag(inode, EXT4_INODE_FLAG_EXTENTS);
698
699                 /* Initialize extent root header */
700                 ext4_extent_tree_init(inode_ref);
701         }
702 #endif
703 }
704
705 static uint32_t ext4_fs_correspond_inode_mode(int filetype)
706 {
707         switch (filetype) {
708         case EXT4_DIRENTRY_DIR:
709                 return EXT4_INODE_MODE_DIRECTORY;
710         case EXT4_DIRENTRY_REG_FILE:
711                 return EXT4_INODE_MODE_FILE;
712         case EXT4_DIRENTRY_SYMLINK:
713                 return EXT4_INODE_MODE_SOFTLINK;
714         default:
715                 /* FIXME: right now we only support 3 file type. */
716                 ext4_assert(0);
717         }
718         return 0;
719 }
720
721 int ext4_fs_alloc_inode(struct ext4_fs *fs, struct ext4_inode_ref *inode_ref,
722                         int filetype)
723 {
724         /* Check if newly allocated i-node will be a directory */
725         bool is_dir;
726
727         is_dir = (filetype == EXT4_DIRENTRY_DIR);
728
729         /* Allocate inode by allocation algorithm */
730         uint32_t index;
731         int rc = ext4_ialloc_alloc_inode(fs, &index, is_dir);
732         if (rc != EOK)
733                 return rc;
734
735         /* Load i-node from on-disk i-node table */
736         rc = ext4_fs_get_inode_ref(fs, index, inode_ref);
737         if (rc != EOK) {
738                 ext4_ialloc_free_inode(fs, index, is_dir);
739                 return rc;
740         }
741
742         /* Initialize i-node */
743         struct ext4_inode *inode = inode_ref->inode;
744
745         uint32_t mode;
746         if (is_dir) {
747                 /*
748                  * Default directory permissions to be compatible with other
749                  * systems
750                  * 0777 (octal) == rwxrwxrwx
751                  */
752
753                 mode = 0777;
754                 mode |= EXT4_INODE_MODE_DIRECTORY;
755         } else {
756                 /*
757                  * Default file permissions to be compatible with other systems
758                  * 0666 (octal) == rw-rw-rw-
759                  */
760
761                 mode = 0666;
762                 mode |= ext4_fs_correspond_inode_mode(filetype);
763         }
764         ext4_inode_set_mode(&fs->sb, inode, mode);
765
766         ext4_inode_set_links_count(inode, 0);
767         ext4_inode_set_uid(inode, 0);
768         ext4_inode_set_gid(inode, 0);
769         ext4_inode_set_size(inode, 0);
770         ext4_inode_set_access_time(inode, 0);
771         ext4_inode_set_change_inode_time(inode, 0);
772         ext4_inode_set_modification_time(inode, 0);
773         ext4_inode_set_deletion_time(inode, 0);
774         ext4_inode_set_blocks_count(&fs->sb, inode, 0);
775         ext4_inode_set_flags(inode, 0);
776         ext4_inode_set_generation(inode, 0);
777
778         /* Reset blocks array. For symbolic link inode, just
779          * fill in blocks with 0 */
780         if (ext4_inode_is_type(&fs->sb, inode, EXT4_INODE_MODE_SOFTLINK)) {
781                 for (int i = 0; i < EXT4_INODE_BLOCKS; i++)
782                         inode->blocks[i] = 0;
783
784         } else
785                 ext4_fs_inode_blocks_init(fs, inode_ref);
786
787         inode_ref->dirty = true;
788
789         return EOK;
790 }
791
792 int ext4_fs_free_inode(struct ext4_inode_ref *inode_ref)
793 {
794         struct ext4_fs *fs = inode_ref->fs;
795         uint32_t offset;
796         uint32_t suboff;
797         int rc;
798 #if CONFIG_EXTENT_ENABLE
799         /* For extents must be data block destroyed by other way */
800         if ((ext4_sb_has_feature_incompatible(&fs->sb,
801                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
802             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
803                 /* Data structures are released during truncate operation... */
804                 goto finish;
805         }
806 #endif
807         /* Release all indirect (no data) blocks */
808
809         /* 1) Single indirect */
810         ext4_fsblk_t fblock = ext4_inode_get_indirect_block(inode_ref->inode, 0);
811         if (fblock != 0) {
812                 int rc = ext4_balloc_free_block(inode_ref, fblock);
813                 if (rc != EOK)
814                         return rc;
815
816                 ext4_inode_set_indirect_block(inode_ref->inode, 0, 0);
817         }
818
819         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
820         uint32_t count = block_size / sizeof(uint32_t);
821
822         struct ext4_block block;
823
824         /* 2) Double indirect */
825         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 1);
826         if (fblock != 0) {
827                 int rc = ext4_block_get(fs->bdev, &block, fblock);
828                 if (rc != EOK)
829                         return rc;
830
831                 ext4_fsblk_t ind_block;
832                 for (offset = 0; offset < count; ++offset) {
833                         ind_block = to_le32(((uint32_t *)block.data)[offset]);
834
835                         if (ind_block == 0)
836                                 continue;
837                         rc = ext4_balloc_free_block(inode_ref, ind_block);
838                         if (rc != EOK) {
839                                 ext4_block_set(fs->bdev, &block);
840                                 return rc;
841                         }
842
843                 }
844
845                 ext4_block_set(fs->bdev, &block);
846                 rc = ext4_balloc_free_block(inode_ref, fblock);
847                 if (rc != EOK)
848                         return rc;
849
850                 ext4_inode_set_indirect_block(inode_ref->inode, 1, 0);
851         }
852
853         /* 3) Tripple indirect */
854         struct ext4_block subblock;
855         fblock = ext4_inode_get_indirect_block(inode_ref->inode, 2);
856         if (fblock == 0)
857                 goto finish;
858         rc = ext4_block_get(fs->bdev, &block, fblock);
859         if (rc != EOK)
860                 return rc;
861
862         ext4_fsblk_t ind_block;
863         for (offset = 0; offset < count; ++offset) {
864                 ind_block = to_le32(((uint32_t *)block.data)[offset]);
865
866                 if (ind_block == 0)
867                         continue;
868                 rc = ext4_block_get(fs->bdev, &subblock,
869                                 ind_block);
870                 if (rc != EOK) {
871                         ext4_block_set(fs->bdev, &block);
872                         return rc;
873                 }
874
875                 ext4_fsblk_t ind_subblk;
876                 for (suboff = 0; suboff < count; ++suboff) {
877                         ind_subblk = to_le32(((uint32_t *)subblock.data)[suboff]);
878
879                         if (ind_subblk == 0)
880                                 continue;
881                         rc = ext4_balloc_free_block(inode_ref, ind_subblk);
882                         if (rc != EOK) {
883                                 ext4_block_set(fs->bdev, &subblock);
884                                 ext4_block_set(fs->bdev, &block);
885                                 return rc;
886                         }
887
888                 }
889
890                 ext4_block_set(fs->bdev, &subblock);
891
892                 rc = ext4_balloc_free_block(inode_ref,
893                                 ind_block);
894                 if (rc != EOK) {
895                         ext4_block_set(fs->bdev, &block);
896                         return rc;
897                 }
898
899         }
900
901         ext4_block_set(fs->bdev, &block);
902         rc = ext4_balloc_free_block(inode_ref, fblock);
903         if (rc != EOK)
904                 return rc;
905
906         ext4_inode_set_indirect_block(inode_ref->inode, 2, 0);
907 finish:
908         /* Mark inode dirty for writing to the physical device */
909         inode_ref->dirty = true;
910
911         /* Free block with extended attributes if present */
912         ext4_fsblk_t xattr_block =
913             ext4_inode_get_file_acl(inode_ref->inode, &fs->sb);
914         if (xattr_block) {
915                 int rc = ext4_balloc_free_block(inode_ref, xattr_block);
916                 if (rc != EOK)
917                         return rc;
918
919                 ext4_inode_set_file_acl(inode_ref->inode, &fs->sb, 0);
920         }
921
922         /* Free inode by allocator */
923         if (ext4_inode_is_type(&fs->sb, inode_ref->inode,
924                                EXT4_INODE_MODE_DIRECTORY))
925                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, true);
926         else
927                 rc = ext4_ialloc_free_inode(fs, inode_ref->index, false);
928
929         return rc;
930 }
931
932 int ext4_fs_truncate_inode(struct ext4_inode_ref *inode_ref, uint64_t new_size)
933 {
934         struct ext4_sblock *sb = &inode_ref->fs->sb;
935         uint32_t i;
936
937         /* Check flags, if i-node can be truncated */
938         if (!ext4_inode_can_truncate(sb, inode_ref->inode))
939                 return EINVAL;
940
941         /* If sizes are equal, nothing has to be done. */
942         uint64_t old_size = ext4_inode_get_size(sb, inode_ref->inode);
943         if (old_size == new_size)
944                 return EOK;
945
946         /* It's not supported to make the larger file by truncate operation */
947         if (old_size < new_size)
948                 return EINVAL;
949
950         if (ext4_inode_is_type(sb, inode_ref->inode, EXT4_INODE_MODE_SOFTLINK)
951                         && old_size < sizeof(inode_ref->inode->blocks)
952                         && !ext4_inode_get_blocks_count(sb, inode_ref->inode)) {
953                 char *content = (char *)inode_ref->inode->blocks;
954                 memset(content + new_size, 0,
955                         sizeof(inode_ref->inode->blocks) - new_size);
956                 ext4_inode_set_size(inode_ref->inode, new_size);
957                 inode_ref->dirty = true;
958
959                 return EOK;
960         }
961
962         /* Compute how many blocks will be released */
963         uint32_t block_size = ext4_sb_get_block_size(sb);
964         uint32_t new_blocks_count = (new_size + block_size - 1) /
965                                     block_size;
966         uint32_t old_blocks_count = (old_size + block_size - 1) /
967                                     block_size;
968         uint32_t diff_blocks_count = old_blocks_count - new_blocks_count;
969 #if CONFIG_EXTENT_ENABLE
970         if ((ext4_sb_has_feature_incompatible(sb,
971                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
972             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
973
974                 /* Extents require special operation */
975                 if (diff_blocks_count) {
976                         int rc = ext4_extent_remove_space(inode_ref,
977                                         new_blocks_count, EXT_MAX_BLOCKS);
978                         if (rc != EOK)
979                                 return rc;
980
981                 }
982         } else
983 #endif
984         {
985                 /* Release data blocks from the end of file */
986
987                 /* Starting from 1 because of logical blocks are numbered from 0
988                  */
989                 for (i = 0; i < diff_blocks_count; ++i) {
990                         int rc = ext4_fs_release_inode_block(
991                             inode_ref, new_blocks_count + i);
992                         if (rc != EOK)
993                                 return rc;
994                 }
995         }
996
997         /* Update i-node */
998         ext4_inode_set_size(inode_ref->inode, new_size);
999         inode_ref->dirty = true;
1000
1001         return EOK;
1002 }
1003
1004 /**@brief Compute 'goal' for inode index
1005  * @param inode_ref Reference to inode, to allocate block for
1006  * @return goal
1007  */
1008 ext4_fsblk_t ext4_fs_inode_to_goal_block(struct ext4_inode_ref *inode_ref)
1009 {
1010         uint32_t group_inodes =
1011                 ext4_get32(&inode_ref->fs->sb, inodes_per_group);
1012         return (inode_ref->index - 1) / group_inodes;
1013 }
1014
1015 /**@brief Compute 'goal' for allocation algorithm (For blockmap).
1016  * @param inode_ref Reference to inode, to allocate block for
1017  * @param goal
1018  * @return error code
1019  */
1020 int ext4_fs_indirect_find_goal(struct ext4_inode_ref *inode_ref,
1021                                 ext4_fsblk_t *goal)
1022 {
1023         struct ext4_sblock *sb = &inode_ref->fs->sb;
1024         *goal = 0;
1025
1026         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1027         uint32_t block_size = ext4_sb_get_block_size(sb);
1028         uint32_t inode_block_count = inode_size / block_size;
1029
1030         if (inode_size % block_size != 0)
1031                 inode_block_count++;
1032
1033         /* If inode has some blocks, get last block address + 1 */
1034         if (inode_block_count > 0) {
1035                 int rc = ext4_fs_get_inode_data_block_index(
1036                     inode_ref, inode_block_count - 1, goal, false);
1037                 if (rc != EOK)
1038                         return rc;
1039
1040                 if (*goal != 0) {
1041                         (*goal)++;
1042                         return rc;
1043                 }
1044
1045                 /* If goal == 0, sparse file -> continue */
1046         }
1047
1048         /* Identify block group of inode */
1049
1050         uint32_t inodes_per_group = ext4_get32(sb, inodes_per_group);
1051         uint32_t block_group = (inode_ref->index - 1) / inodes_per_group;
1052         block_size = ext4_sb_get_block_size(sb);
1053
1054         /* Load block group reference */
1055         struct ext4_block_group_ref bg_ref;
1056         int rc =
1057             ext4_fs_get_block_group_ref(inode_ref->fs, block_group, &bg_ref);
1058         if (rc != EOK)
1059                 return rc;
1060
1061         /* Compute indexes */
1062         uint32_t block_group_count = ext4_block_group_cnt(sb);
1063         ext4_fsblk_t inode_table_first_block =
1064             ext4_bg_get_inode_table_first_block(bg_ref.block_group, sb);
1065         uint16_t inode_table_item_size = ext4_get16(sb, inode_size);
1066         uint32_t inode_table_bytes;
1067
1068         /* Check for last block group */
1069         if (block_group < block_group_count - 1) {
1070                 inode_table_bytes = inodes_per_group * inode_table_item_size;
1071         } else {
1072                 /* Last block group could be smaller */
1073                 uint32_t inodes_count_total = ext4_get32(sb, inodes_count);
1074
1075                 inode_table_bytes =
1076                     (inodes_count_total -
1077                      ((block_group_count - 1) * inodes_per_group)) *
1078                     inode_table_item_size;
1079         }
1080
1081         ext4_fsblk_t inode_table_blocks = inode_table_bytes / block_size;
1082
1083         if (inode_table_bytes % block_size)
1084                 inode_table_blocks++;
1085
1086         *goal = inode_table_first_block + inode_table_blocks;
1087
1088         return ext4_fs_put_block_group_ref(&bg_ref);
1089 }
1090
1091 static int ext4_fs_get_inode_data_block_idx(struct ext4_inode_ref *inode_ref,
1092                                        uint64_t iblock, ext4_fsblk_t *fblock,
1093                                        bool extent_create,
1094                                        bool support_unwritten)
1095 {
1096         struct ext4_fs *fs = inode_ref->fs;
1097
1098         /* For empty file is situation simple */
1099         if (ext4_inode_get_size(&fs->sb, inode_ref->inode) == 0) {
1100                 *fblock = 0;
1101                 return EOK;
1102         }
1103
1104         ext4_fsblk_t current_block;
1105
1106         (void)extent_create;
1107 #if CONFIG_EXTENT_ENABLE
1108         /* Handle i-node using extents */
1109         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1110                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1111             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1112
1113                 ext4_fsblk_t current_fsblk;
1114                 int rc = ext4_extent_get_blocks(inode_ref, iblock, 1,
1115                                 &current_fsblk, extent_create, NULL);
1116                 if (rc != EOK)
1117                         return rc;
1118
1119                 current_block = current_fsblk;
1120                 *fblock = current_block;
1121
1122                 ext4_assert(*fblock || support_unwritten);
1123                 return EOK;
1124         }
1125 #endif
1126
1127         struct ext4_inode *inode = inode_ref->inode;
1128
1129         /* Direct block are read directly from array in i-node structure */
1130         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1131                 current_block =
1132                     ext4_inode_get_direct_block(inode, (uint32_t)iblock);
1133                 *fblock = current_block;
1134                 return EOK;
1135         }
1136
1137         /* Determine indirection level of the target block */
1138         unsigned int level = 0;
1139         unsigned int i;
1140         for (i = 1; i < 4; i++) {
1141                 if (iblock < fs->inode_block_limits[i]) {
1142                         level = i;
1143                         break;
1144                 }
1145         }
1146
1147         if (level == 0)
1148                 return EIO;
1149
1150         /* Compute offsets for the topmost level */
1151         uint64_t block_offset_in_level =
1152             iblock - fs->inode_block_limits[level - 1];
1153         current_block = ext4_inode_get_indirect_block(inode, level - 1);
1154         uint32_t offset_in_block =
1155             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1156
1157         /* Sparse file */
1158         if (current_block == 0) {
1159                 *fblock = 0;
1160                 return EOK;
1161         }
1162
1163         struct ext4_block block;
1164
1165         /*
1166          * Navigate through other levels, until we find the block number
1167          * or find null reference meaning we are dealing with sparse file
1168          */
1169         while (level > 0) {
1170                 /* Load indirect block */
1171                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1172                 if (rc != EOK)
1173                         return rc;
1174
1175                 /* Read block address from indirect block */
1176                 current_block =
1177                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1178
1179                 /* Put back indirect block untouched */
1180                 rc = ext4_block_set(fs->bdev, &block);
1181                 if (rc != EOK)
1182                         return rc;
1183
1184                 /* Check for sparse file */
1185                 if (current_block == 0) {
1186                         *fblock = 0;
1187                         return EOK;
1188                 }
1189
1190                 /* Jump to the next level */
1191                 level--;
1192
1193                 /* Termination condition - we have address of data block loaded
1194                  */
1195                 if (level == 0)
1196                         break;
1197
1198                 /* Visit the next level */
1199                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1200                 offset_in_block = block_offset_in_level /
1201                                   fs->inode_blocks_per_level[level - 1];
1202         }
1203
1204         *fblock = current_block;
1205
1206         return EOK;
1207 }
1208
1209
1210 int ext4_fs_get_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1211                                        uint64_t iblock, ext4_fsblk_t *fblock,
1212                                        bool support_unwritten)
1213 {
1214         return ext4_fs_get_inode_data_block_idx(inode_ref, iblock, fblock,
1215                         false, support_unwritten);
1216 }
1217
1218 int ext4_fs_init_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1219                                        uint64_t iblock, ext4_fsblk_t *fblock)
1220 {
1221         return ext4_fs_get_inode_data_block_idx(inode_ref, iblock, fblock,
1222                         true, true);
1223 }
1224
1225 int ext4_fs_set_inode_data_block_index(struct ext4_inode_ref *inode_ref,
1226                                        uint64_t iblock, ext4_fsblk_t fblock)
1227 {
1228         struct ext4_fs *fs = inode_ref->fs;
1229
1230 #if CONFIG_EXTENT_ENABLE
1231         /* Handle inode using extents */
1232         if ((ext4_sb_has_feature_incompatible(&fs->sb,
1233                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1234             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1235                 /* Not reachable */
1236                 return ENOTSUP;
1237         }
1238 #endif
1239
1240         /* Handle simple case when we are dealing with direct reference */
1241         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1242                 ext4_inode_set_direct_block(inode_ref->inode, (uint32_t)iblock,
1243                                             (uint32_t)fblock);
1244                 inode_ref->dirty = true;
1245
1246                 return EOK;
1247         }
1248
1249         /* Determine the indirection level needed to get the desired block */
1250         unsigned int level = 0;
1251         unsigned int i;
1252         for (i = 1; i < 4; i++) {
1253                 if (iblock < fs->inode_block_limits[i]) {
1254                         level = i;
1255                         break;
1256                 }
1257         }
1258
1259         if (level == 0)
1260                 return EIO;
1261
1262         uint32_t block_size = ext4_sb_get_block_size(&fs->sb);
1263
1264         /* Compute offsets for the topmost level */
1265         uint64_t block_offset_in_level =
1266             iblock - fs->inode_block_limits[level - 1];
1267         ext4_fsblk_t current_block =
1268             ext4_inode_get_indirect_block(inode_ref->inode, level - 1);
1269         uint32_t offset_in_block =
1270             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1271
1272         ext4_fsblk_t new_block_addr;
1273
1274         struct ext4_block block;
1275         struct ext4_block new_block;
1276
1277         /* Is needed to allocate indirect block on the i-node level */
1278         if (current_block == 0) {
1279                 /* Allocate new indirect block */
1280                 ext4_fsblk_t goal;
1281                 int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1282                 if (rc != EOK)
1283                         return rc;
1284
1285                 rc = ext4_balloc_alloc_block(inode_ref,
1286                                              goal,
1287                                              &new_block_addr);
1288                 if (rc != EOK)
1289                         return rc;
1290
1291                 /* Update i-node */
1292                 ext4_inode_set_indirect_block(inode_ref->inode, level - 1,
1293                                               (uint32_t)new_block_addr);
1294                 inode_ref->dirty = true;
1295
1296                 /* Load newly allocated block */
1297                 rc = ext4_block_get(fs->bdev, &new_block, new_block_addr);
1298                 if (rc != EOK) {
1299                         ext4_balloc_free_block(inode_ref, new_block_addr);
1300                         return rc;
1301                 }
1302
1303                 /* Initialize new block */
1304                 memset(new_block.data, 0, block_size);
1305                 new_block.dirty = true;
1306
1307                 /* Put back the allocated block */
1308                 rc = ext4_block_set(fs->bdev, &new_block);
1309                 if (rc != EOK)
1310                         return rc;
1311
1312                 current_block = new_block_addr;
1313         }
1314
1315         /*
1316          * Navigate through other levels, until we find the block number
1317          * or find null reference meaning we are dealing with sparse file
1318          */
1319         while (level > 0) {
1320                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1321                 if (rc != EOK)
1322                         return rc;
1323
1324                 current_block =
1325                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1326
1327                 if ((level > 1) && (current_block == 0)) {
1328                         ext4_fsblk_t goal;
1329                         rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1330                         if (rc != EOK) {
1331                                 ext4_block_set(fs->bdev, &block);
1332                                 return rc;
1333                         }
1334
1335                         /* Allocate new block */
1336                         rc =
1337                             ext4_balloc_alloc_block(inode_ref, goal, &new_block_addr);
1338                         if (rc != EOK) {
1339                                 ext4_block_set(fs->bdev, &block);
1340                                 return rc;
1341                         }
1342
1343                         /* Load newly allocated block */
1344                         rc = ext4_block_get(fs->bdev, &new_block,
1345                                             new_block_addr);
1346
1347                         if (rc != EOK) {
1348                                 ext4_block_set(fs->bdev, &block);
1349                                 return rc;
1350                         }
1351
1352                         /* Initialize allocated block */
1353                         memset(new_block.data, 0, block_size);
1354                         new_block.dirty = true;
1355
1356                         rc = ext4_block_set(fs->bdev, &new_block);
1357                         if (rc != EOK) {
1358                                 ext4_block_set(fs->bdev, &block);
1359                                 return rc;
1360                         }
1361
1362                         /* Write block address to the parent */
1363                         ((uint32_t *)block.data)[offset_in_block] =
1364                             to_le32((uint32_t)new_block_addr);
1365                         block.dirty = true;
1366                         current_block = new_block_addr;
1367                 }
1368
1369                 /* Will be finished, write the fblock address */
1370                 if (level == 1) {
1371                         ((uint32_t *)block.data)[offset_in_block] =
1372                             to_le32((uint32_t)fblock);
1373                         block.dirty = true;
1374                 }
1375
1376                 rc = ext4_block_set(fs->bdev, &block);
1377                 if (rc != EOK)
1378                         return rc;
1379
1380                 level--;
1381
1382                 /*
1383                  * If we are on the last level, break here as
1384                  * there is no next level to visit
1385                  */
1386                 if (level == 0)
1387                         break;
1388
1389                 /* Visit the next level */
1390                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1391                 offset_in_block = block_offset_in_level /
1392                                   fs->inode_blocks_per_level[level - 1];
1393         }
1394
1395         return EOK;
1396 }
1397
1398 int ext4_fs_release_inode_block(struct ext4_inode_ref *inode_ref,
1399                                 uint32_t iblock)
1400 {
1401         ext4_fsblk_t fblock;
1402
1403         struct ext4_fs *fs = inode_ref->fs;
1404
1405         /* Extents are handled otherwise = there is not support in this function
1406          */
1407         ext4_assert(!(
1408             ext4_sb_has_feature_incompatible(&fs->sb,
1409                                              EXT4_FEATURE_INCOMPAT_EXTENTS) &&
1410             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))));
1411
1412         struct ext4_inode *inode = inode_ref->inode;
1413
1414         /* Handle simple case when we are dealing with direct reference */
1415         if (iblock < EXT4_INODE_DIRECT_BLOCK_COUNT) {
1416                 fblock = ext4_inode_get_direct_block(inode, iblock);
1417
1418                 /* Sparse file */
1419                 if (fblock == 0)
1420                         return EOK;
1421
1422                 ext4_inode_set_direct_block(inode, iblock, 0);
1423                 return ext4_balloc_free_block(inode_ref, fblock);
1424         }
1425
1426         /* Determine the indirection level needed to get the desired block */
1427         unsigned int level = 0;
1428         unsigned int i;
1429         for (i = 1; i < 4; i++) {
1430                 if (iblock < fs->inode_block_limits[i]) {
1431                         level = i;
1432                         break;
1433                 }
1434         }
1435
1436         if (level == 0)
1437                 return EIO;
1438
1439         /* Compute offsets for the topmost level */
1440         uint64_t block_offset_in_level =
1441             iblock - fs->inode_block_limits[level - 1];
1442         ext4_fsblk_t current_block =
1443             ext4_inode_get_indirect_block(inode, level - 1);
1444         uint32_t offset_in_block =
1445             block_offset_in_level / fs->inode_blocks_per_level[level - 1];
1446
1447         /*
1448          * Navigate through other levels, until we find the block number
1449          * or find null reference meaning we are dealing with sparse file
1450          */
1451         struct ext4_block block;
1452
1453         while (level > 0) {
1454
1455                 /* Sparse check */
1456                 if (current_block == 0)
1457                         return EOK;
1458
1459                 int rc = ext4_block_get(fs->bdev, &block, current_block);
1460                 if (rc != EOK)
1461                         return rc;
1462
1463                 current_block =
1464                     to_le32(((uint32_t *)block.data)[offset_in_block]);
1465
1466                 /* Set zero if physical data block address found */
1467                 if (level == 1) {
1468                         ((uint32_t *)block.data)[offset_in_block] = to_le32(0);
1469                         block.dirty = true;
1470                 }
1471
1472                 rc = ext4_block_set(fs->bdev, &block);
1473                 if (rc != EOK)
1474                         return rc;
1475
1476                 level--;
1477
1478                 /*
1479                  * If we are on the last level, break here as
1480                  * there is no next level to visit
1481                  */
1482                 if (level == 0)
1483                         break;
1484
1485                 /* Visit the next level */
1486                 block_offset_in_level %= fs->inode_blocks_per_level[level];
1487                 offset_in_block = block_offset_in_level /
1488                                   fs->inode_blocks_per_level[level - 1];
1489         }
1490
1491         fblock = current_block;
1492         if (fblock == 0)
1493                 return EOK;
1494
1495         /* Physical block is not referenced, it can be released */
1496         return ext4_balloc_free_block(inode_ref, fblock);
1497 }
1498
1499 int ext4_fs_append_inode_block(struct ext4_inode_ref *inode_ref,
1500                                ext4_fsblk_t *fblock, uint32_t *iblock)
1501 {
1502 #if CONFIG_EXTENT_ENABLE
1503         /* Handle extents separately */
1504         if ((ext4_sb_has_feature_incompatible(&inode_ref->fs->sb,
1505                                               EXT4_FEATURE_INCOMPAT_EXTENTS)) &&
1506             (ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_EXTENTS))) {
1507                 int rc;
1508                 ext4_fsblk_t current_fsblk;
1509                 struct ext4_sblock *sb = &inode_ref->fs->sb;
1510                 uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1511                 uint32_t block_size = ext4_sb_get_block_size(sb);
1512                 *iblock = (inode_size + block_size - 1) /
1513                                     block_size;
1514
1515                 rc = ext4_extent_get_blocks(inode_ref, *iblock, 1,
1516                                 &current_fsblk, true, NULL);
1517
1518
1519                 *fblock = current_fsblk;
1520                 ext4_assert(*fblock);
1521
1522                 ext4_inode_set_size(inode_ref->inode,
1523                                     inode_size + block_size);
1524                 inode_ref->dirty = true;
1525
1526
1527                 return rc;
1528         }
1529 #endif
1530         struct ext4_sblock *sb = &inode_ref->fs->sb;
1531
1532         /* Compute next block index and allocate data block */
1533         uint64_t inode_size = ext4_inode_get_size(sb, inode_ref->inode);
1534         uint32_t block_size = ext4_sb_get_block_size(sb);
1535
1536         /* Align size i-node size */
1537         if ((inode_size % block_size) != 0)
1538                 inode_size += block_size - (inode_size % block_size);
1539
1540         /* Logical blocks are numbered from 0 */
1541         uint32_t new_block_idx = inode_size / block_size;
1542
1543         /* Allocate new physical block */
1544         ext4_fsblk_t goal, phys_block;
1545         int rc = ext4_fs_indirect_find_goal(inode_ref, &goal);
1546         if (rc != EOK)
1547                 return rc;
1548
1549         rc = ext4_balloc_alloc_block(inode_ref, goal, &phys_block);
1550         if (rc != EOK)
1551                 return rc;
1552
1553         /* Add physical block address to the i-node */
1554         rc = ext4_fs_set_inode_data_block_index(inode_ref, new_block_idx,
1555                                                 phys_block);
1556         if (rc != EOK) {
1557                 ext4_balloc_free_block(inode_ref, phys_block);
1558                 return rc;
1559         }
1560
1561         /* Update i-node */
1562         ext4_inode_set_size(inode_ref->inode, inode_size + block_size);
1563         inode_ref->dirty = true;
1564
1565         *fblock = phys_block;
1566         *iblock = new_block_idx;
1567
1568         return EOK;
1569 }
1570
1571 void ext4_fs_inode_links_count_inc(struct ext4_inode_ref *inode_ref)
1572 {
1573         uint16_t link;
1574
1575         link = ext4_inode_get_links_count(inode_ref->inode);
1576         link++;
1577         ext4_inode_set_links_count(inode_ref->inode, link);
1578
1579         bool is_dx =
1580             ext4_sb_has_feature_compatible(&inode_ref->fs->sb,
1581                                            EXT4_FEATURE_COMPAT_DIR_INDEX) &&
1582             ext4_inode_has_flag(inode_ref->inode, EXT4_INODE_FLAG_INDEX);
1583
1584         if (is_dx && link > 1) {
1585                 if (link >= EXT4_LINK_MAX || link == 2) {
1586                         ext4_inode_set_links_count(inode_ref->inode, 1);
1587
1588                         uint32_t v =
1589                             ext4_get32(&inode_ref->fs->sb, features_read_only);
1590                         v |= EXT4_FEATURE_RO_COMPAT_DIR_NLINK;
1591                         ext4_set32(&inode_ref->fs->sb, features_read_only, v);
1592                 }
1593         }
1594 }
1595
1596 void ext4_fs_inode_links_count_dec(struct ext4_inode_ref *inode_ref)
1597 {
1598         uint16_t links = ext4_inode_get_links_count(inode_ref->inode);
1599         if (!ext4_inode_is_type(&inode_ref->fs->sb, inode_ref->inode,
1600                                 EXT4_INODE_MODE_DIRECTORY)) {
1601                 if (links > 0)
1602                         ext4_inode_set_links_count(inode_ref->inode, links - 1);
1603                 return;
1604         }
1605
1606         if (links > 2)
1607                 ext4_inode_set_links_count(inode_ref->inode, links - 1);
1608 }
1609
1610 /**
1611  * @}
1612  */