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