1 /*
2  * Copyright (c) 2023 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/init.h>
9 #include <zephyr/fs/fs.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/sys/byteorder.h>
13 
14 #include <stdint.h>
15 
16 #include "ext2.h"
17 #include "ext2_struct.h"
18 #include "ext2_impl.h"
19 #include "ext2_diskops.h"
20 #include "ext2_bitmap.h"
21 
22 LOG_MODULE_DECLARE(ext2);
23 
24 /* Static declarations */
25 static int get_level_offsets(struct ext2_data *fs, uint32_t block, uint32_t offsets[4]);
26 static inline uint32_t get_ngroups(struct ext2_data *fs);
27 
28 #define MAX_OFFSETS_SIZE 4
29 /* Array of zeros to be used in inode block calculation */
30 static const uint32_t zero_offsets[MAX_OFFSETS_SIZE];
31 
fill_sblock(struct ext2_superblock * sb,struct ext2_disk_superblock * disk_sb)32 static void fill_sblock(struct ext2_superblock *sb, struct ext2_disk_superblock *disk_sb)
33 {
34 	sb->s_inodes_count      = sys_le32_to_cpu(disk_sb->s_inodes_count);
35 	sb->s_blocks_count      = sys_le32_to_cpu(disk_sb->s_blocks_count);
36 	sb->s_free_blocks_count = sys_le32_to_cpu(disk_sb->s_free_blocks_count);
37 	sb->s_free_inodes_count = sys_le32_to_cpu(disk_sb->s_free_inodes_count);
38 	sb->s_first_data_block  = sys_le32_to_cpu(disk_sb->s_first_data_block);
39 	sb->s_log_block_size    = sys_le32_to_cpu(disk_sb->s_log_block_size);
40 	sb->s_log_frag_size     = sys_le32_to_cpu(disk_sb->s_log_frag_size);
41 	sb->s_blocks_per_group  = sys_le32_to_cpu(disk_sb->s_blocks_per_group);
42 	sb->s_frags_per_group   = sys_le32_to_cpu(disk_sb->s_frags_per_group);
43 	sb->s_inodes_per_group  = sys_le32_to_cpu(disk_sb->s_inodes_per_group);
44 	sb->s_mnt_count         = sys_le16_to_cpu(disk_sb->s_mnt_count);
45 	sb->s_max_mnt_count     = sys_le16_to_cpu(disk_sb->s_max_mnt_count);
46 	sb->s_magic             = sys_le16_to_cpu(disk_sb->s_magic);
47 	sb->s_state             = sys_le16_to_cpu(disk_sb->s_state);
48 	sb->s_errors            = sys_le16_to_cpu(disk_sb->s_errors);
49 	sb->s_creator_os        = sys_le32_to_cpu(disk_sb->s_creator_os);
50 	sb->s_rev_level         = sys_le32_to_cpu(disk_sb->s_rev_level);
51 	sb->s_first_ino         = sys_le32_to_cpu(disk_sb->s_first_ino);
52 	sb->s_inode_size        = sys_le16_to_cpu(disk_sb->s_inode_size);
53 	sb->s_block_group_nr    = sys_le16_to_cpu(disk_sb->s_block_group_nr);
54 	sb->s_feature_compat    = sys_le32_to_cpu(disk_sb->s_feature_compat);
55 	sb->s_feature_incompat  = sys_le32_to_cpu(disk_sb->s_feature_incompat);
56 	sb->s_feature_ro_compat = sys_le32_to_cpu(disk_sb->s_feature_ro_compat);
57 }
58 
fill_disk_sblock(struct ext2_disk_superblock * disk_sb,struct ext2_superblock * sb)59 static void fill_disk_sblock(struct ext2_disk_superblock *disk_sb, struct ext2_superblock *sb)
60 {
61 	disk_sb->s_inodes_count      = sys_cpu_to_le32(sb->s_inodes_count);
62 	disk_sb->s_blocks_count      = sys_cpu_to_le32(sb->s_blocks_count);
63 	disk_sb->s_free_blocks_count = sys_cpu_to_le32(sb->s_free_blocks_count);
64 	disk_sb->s_free_inodes_count = sys_cpu_to_le32(sb->s_free_inodes_count);
65 	disk_sb->s_first_data_block  = sys_cpu_to_le32(sb->s_first_data_block);
66 	disk_sb->s_log_block_size    = sys_cpu_to_le32(sb->s_log_block_size);
67 	disk_sb->s_log_frag_size     = sys_cpu_to_le32(sb->s_log_frag_size);
68 	disk_sb->s_blocks_per_group  = sys_cpu_to_le32(sb->s_blocks_per_group);
69 	disk_sb->s_frags_per_group   = sys_cpu_to_le32(sb->s_frags_per_group);
70 	disk_sb->s_inodes_per_group  = sys_cpu_to_le32(sb->s_inodes_per_group);
71 	disk_sb->s_mnt_count         = sys_cpu_to_le16(sb->s_mnt_count);
72 	disk_sb->s_max_mnt_count     = sys_cpu_to_le16(sb->s_max_mnt_count);
73 	disk_sb->s_magic             = sys_cpu_to_le16(sb->s_magic);
74 	disk_sb->s_state             = sys_cpu_to_le16(sb->s_state);
75 	disk_sb->s_errors            = sys_cpu_to_le16(sb->s_errors);
76 	disk_sb->s_creator_os        = sys_cpu_to_le32(sb->s_creator_os);
77 	disk_sb->s_rev_level         = sys_cpu_to_le32(sb->s_rev_level);
78 	disk_sb->s_first_ino         = sys_cpu_to_le32(sb->s_first_ino);
79 	disk_sb->s_inode_size        = sys_cpu_to_le16(sb->s_inode_size);
80 	disk_sb->s_block_group_nr    = sys_cpu_to_le16(sb->s_block_group_nr);
81 	disk_sb->s_feature_compat    = sys_cpu_to_le32(sb->s_feature_compat);
82 	disk_sb->s_feature_incompat  = sys_cpu_to_le32(sb->s_feature_incompat);
83 	disk_sb->s_feature_ro_compat = sys_cpu_to_le32(sb->s_feature_ro_compat);
84 }
85 
fill_bgroup(struct ext2_bgroup * bg,struct ext2_disk_bgroup * disk_bg)86 static void fill_bgroup(struct ext2_bgroup *bg, struct ext2_disk_bgroup *disk_bg)
87 {
88 	bg->bg_block_bitmap      = sys_le32_to_cpu(disk_bg->bg_block_bitmap);
89 	bg->bg_inode_bitmap      = sys_le32_to_cpu(disk_bg->bg_inode_bitmap);
90 	bg->bg_inode_table       = sys_le32_to_cpu(disk_bg->bg_inode_table);
91 	bg->bg_free_blocks_count = sys_le16_to_cpu(disk_bg->bg_free_blocks_count);
92 	bg->bg_free_inodes_count = sys_le16_to_cpu(disk_bg->bg_free_inodes_count);
93 	bg->bg_used_dirs_count   = sys_le16_to_cpu(disk_bg->bg_used_dirs_count);
94 }
95 
fill_disk_bgroup(struct ext2_disk_bgroup * disk_bg,struct ext2_bgroup * bg)96 static void fill_disk_bgroup(struct ext2_disk_bgroup *disk_bg, struct ext2_bgroup *bg)
97 {
98 	disk_bg->bg_block_bitmap      = sys_cpu_to_le32(bg->bg_block_bitmap);
99 	disk_bg->bg_inode_bitmap      = sys_cpu_to_le32(bg->bg_inode_bitmap);
100 	disk_bg->bg_inode_table       = sys_cpu_to_le32(bg->bg_inode_table);
101 	disk_bg->bg_free_blocks_count = sys_cpu_to_le16(bg->bg_free_blocks_count);
102 	disk_bg->bg_free_inodes_count = sys_cpu_to_le16(bg->bg_free_inodes_count);
103 	disk_bg->bg_used_dirs_count   = sys_cpu_to_le16(bg->bg_used_dirs_count);
104 }
105 
fill_inode(struct ext2_inode * inode,struct ext2_disk_inode * dino)106 static void fill_inode(struct ext2_inode *inode, struct ext2_disk_inode *dino)
107 {
108 	inode->i_mode        = sys_le16_to_cpu(dino->i_mode);
109 	inode->i_size        = sys_le32_to_cpu(dino->i_size);
110 	inode->i_links_count = sys_le16_to_cpu(dino->i_links_count);
111 	inode->i_blocks      = sys_le32_to_cpu(dino->i_blocks);
112 	for (int i = 0; i < EXT2_INODE_BLOCKS; i++) {
113 		inode->i_block[i] = sys_le32_to_cpu(dino->i_block[i]);
114 	}
115 }
116 
fill_disk_inode(struct ext2_disk_inode * dino,struct ext2_inode * inode)117 static void fill_disk_inode(struct ext2_disk_inode *dino, struct ext2_inode *inode)
118 {
119 	dino->i_mode        = sys_cpu_to_le16(inode->i_mode);
120 	dino->i_size        = sys_cpu_to_le32(inode->i_size);
121 	dino->i_links_count = sys_cpu_to_le16(inode->i_links_count);
122 	dino->i_blocks      = sys_cpu_to_le32(inode->i_blocks);
123 	for (int i = 0; i < EXT2_INODE_BLOCKS; i++) {
124 		dino->i_block[i] = sys_cpu_to_le32(inode->i_block[i]);
125 	}
126 }
127 
ext2_fetch_direntry(struct ext2_disk_direntry * disk_de)128 struct ext2_direntry *ext2_fetch_direntry(struct ext2_disk_direntry *disk_de)
129 {
130 
131 	if (disk_de->de_name_len > EXT2_MAX_FILE_NAME) {
132 		return NULL;
133 	}
134 	uint32_t prog_rec_len = sizeof(struct ext2_direntry) + disk_de->de_name_len;
135 	struct ext2_direntry *de = k_heap_alloc(&direntry_heap, prog_rec_len, K_FOREVER);
136 
137 	__ASSERT(de != NULL, "allocated direntry can't be NULL");
138 
139 	de->de_inode     = sys_le32_to_cpu(disk_de->de_inode);
140 	de->de_rec_len   = sys_le16_to_cpu(disk_de->de_rec_len);
141 	de->de_name_len  = disk_de->de_name_len;
142 	de->de_file_type = disk_de->de_file_type;
143 	memcpy(de->de_name, disk_de->de_name, de->de_name_len);
144 	return de;
145 }
146 
ext2_write_direntry(struct ext2_disk_direntry * disk_de,struct ext2_direntry * de)147 void ext2_write_direntry(struct ext2_disk_direntry *disk_de, struct ext2_direntry *de)
148 {
149 	disk_de->de_inode     = sys_le32_to_cpu(de->de_inode);
150 	disk_de->de_rec_len   = sys_le16_to_cpu(de->de_rec_len);
151 	disk_de->de_name_len  = de->de_name_len;
152 	disk_de->de_file_type = de->de_file_type;
153 	memcpy(disk_de->de_name, de->de_name, de->de_name_len);
154 }
155 
ext2_get_disk_direntry_inode(struct ext2_disk_direntry * de)156 uint32_t ext2_get_disk_direntry_inode(struct ext2_disk_direntry *de)
157 {
158 	return sys_le32_to_cpu(de->de_inode);
159 }
160 
ext2_get_disk_direntry_reclen(struct ext2_disk_direntry * de)161 uint32_t ext2_get_disk_direntry_reclen(struct ext2_disk_direntry *de)
162 {
163 	return sys_le16_to_cpu(de->de_rec_len);
164 }
165 
ext2_get_disk_direntry_namelen(struct ext2_disk_direntry * de)166 uint8_t ext2_get_disk_direntry_namelen(struct ext2_disk_direntry *de)
167 {
168 	return de->de_name_len;
169 }
170 
ext2_get_disk_direntry_type(struct ext2_disk_direntry * de)171 uint8_t ext2_get_disk_direntry_type(struct ext2_disk_direntry *de)
172 {
173 	return de->de_file_type;
174 }
175 
ext2_set_disk_direntry_inode(struct ext2_disk_direntry * de,uint32_t inode)176 void ext2_set_disk_direntry_inode(struct ext2_disk_direntry *de, uint32_t inode)
177 {
178 	de->de_inode = sys_cpu_to_le32(inode);
179 }
180 
ext2_set_disk_direntry_reclen(struct ext2_disk_direntry * de,uint16_t reclen)181 void ext2_set_disk_direntry_reclen(struct ext2_disk_direntry *de, uint16_t reclen)
182 {
183 	de->de_rec_len = sys_cpu_to_le16(reclen);
184 }
185 
ext2_set_disk_direntry_namelen(struct ext2_disk_direntry * de,uint8_t namelen)186 void ext2_set_disk_direntry_namelen(struct ext2_disk_direntry *de, uint8_t namelen)
187 {
188 	de->de_name_len = namelen;
189 }
190 
ext2_set_disk_direntry_type(struct ext2_disk_direntry * de,uint8_t type)191 void ext2_set_disk_direntry_type(struct ext2_disk_direntry *de, uint8_t type)
192 {
193 	de->de_file_type = type;
194 }
195 
ext2_set_disk_direntry_name(struct ext2_disk_direntry * de,const char * name,size_t len)196 void ext2_set_disk_direntry_name(struct ext2_disk_direntry *de, const char *name, size_t len)
197 {
198 	memcpy(de->de_name, name, len);
199 }
200 
ext2_fetch_superblock(struct ext2_data * fs)201 int ext2_fetch_superblock(struct ext2_data *fs)
202 {
203 	struct ext2_block *b;
204 	uint32_t sblock_offset;
205 
206 	if (fs->block_size == 1024) {
207 		sblock_offset = 0;
208 		b = ext2_get_block(fs, 1);
209 	} else {
210 		sblock_offset = 1024;
211 		b = ext2_get_block(fs, 0);
212 	}
213 	if (b == NULL) {
214 		return -ENOENT;
215 	}
216 
217 	struct ext2_disk_superblock *disk_sb =
218 		(struct ext2_disk_superblock *)(b->data + sblock_offset);
219 
220 	fill_sblock(&fs->sblock, disk_sb);
221 
222 	ext2_drop_block(b);
223 	return 0;
224 }
225 
get_ngroups(struct ext2_data * fs)226 static inline uint32_t get_ngroups(struct ext2_data *fs)
227 {
228 	uint32_t ngroups =
229 		fs->sblock.s_blocks_count / fs->sblock.s_blocks_per_group;
230 
231 	if (fs->sblock.s_blocks_count % fs->sblock.s_blocks_per_group != 0) {
232 		/* there is one more group if the last group is incomplete */
233 		ngroups += 1;
234 	}
235 	return ngroups;
236 }
237 
ext2_fetch_block_group(struct ext2_data * fs,uint32_t group)238 int ext2_fetch_block_group(struct ext2_data *fs, uint32_t group)
239 {
240 	struct ext2_bgroup *bg = &fs->bgroup;
241 
242 	/* Check if block group is cached */
243 	if (group == bg->num) {
244 		return 0;
245 	}
246 
247 	uint32_t ngroups = get_ngroups(fs);
248 
249 	LOG_DBG("ngroups:%d", ngroups);
250 	LOG_DBG("cur_group:%d fetch_group:%d", bg->num, group);
251 
252 	if (group > ngroups) {
253 		return -ERANGE;
254 	}
255 
256 	uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
257 	uint32_t block = group / groups_per_block;
258 	uint32_t offset = group % groups_per_block;
259 	uint32_t global_block = fs->sblock.s_first_data_block + 1 + block;
260 
261 	struct ext2_block *b = ext2_get_block(fs, global_block);
262 
263 	if (b == NULL) {
264 		return -ENOENT;
265 	}
266 
267 	struct ext2_disk_bgroup *disk_bg = ((struct ext2_disk_bgroup *)b->data) + offset;
268 
269 	fill_bgroup(bg, disk_bg);
270 
271 	/* Drop unused block */
272 	ext2_drop_block(b);
273 
274 	/* Invalidate previously fetched blocks */
275 	ext2_drop_block(bg->inode_table);
276 	ext2_drop_block(bg->inode_bitmap);
277 	ext2_drop_block(bg->block_bitmap);
278 	bg->inode_table = bg->inode_bitmap = bg->block_bitmap = NULL;
279 
280 	bg->fs = fs;
281 	bg->num = group;
282 
283 	LOG_DBG("[BG:%d] itable:%d free_blk:%d free_ino:%d useddirs:%d bbitmap:%d ibitmap:%d",
284 			group, bg->bg_inode_table,
285 			bg->bg_free_blocks_count,
286 			bg->bg_free_inodes_count,
287 			bg->bg_used_dirs_count,
288 			bg->bg_block_bitmap,
289 			bg->bg_inode_bitmap);
290 	return 0;
291 }
292 
ext2_fetch_bg_itable(struct ext2_bgroup * bg,uint32_t block)293 int ext2_fetch_bg_itable(struct ext2_bgroup *bg, uint32_t block)
294 {
295 	if (bg->inode_table && bg->inode_table_block == block) {
296 		return 0;
297 	}
298 
299 	struct ext2_data *fs = bg->fs;
300 	uint32_t global_block = bg->bg_inode_table + block;
301 
302 	ext2_drop_block(bg->inode_table);
303 	bg->inode_table = ext2_get_block(fs, global_block);
304 	if (bg->inode_table == NULL) {
305 		return -ENOENT;
306 	}
307 
308 	bg->inode_table_block = block;
309 	return 0;
310 }
311 
ext2_fetch_bg_ibitmap(struct ext2_bgroup * bg)312 int ext2_fetch_bg_ibitmap(struct ext2_bgroup *bg)
313 {
314 	if (bg->inode_bitmap) {
315 		return 0;
316 	}
317 
318 	struct ext2_data *fs = bg->fs;
319 	uint32_t global_block = bg->bg_inode_bitmap;
320 
321 	bg->inode_bitmap = ext2_get_block(fs, global_block);
322 	if (bg->inode_bitmap == NULL) {
323 		return -ENOENT;
324 	}
325 	return 0;
326 }
327 
ext2_fetch_bg_bbitmap(struct ext2_bgroup * bg)328 int ext2_fetch_bg_bbitmap(struct ext2_bgroup *bg)
329 {
330 	if (bg->block_bitmap) {
331 		return 0;
332 	}
333 
334 	struct ext2_data *fs = bg->fs;
335 	uint32_t global_block = bg->bg_block_bitmap;
336 
337 	bg->block_bitmap = ext2_get_block(fs, global_block);
338 	if (bg->block_bitmap == NULL) {
339 		return -ENOENT;
340 	}
341 	return 0;
342 }
343 
344 /**
345  * @brief Fetch block group and inode table of given inode.
346  *
347  * @return Offset of inode in currently fetched inode table block.
348  */
get_itable_entry(struct ext2_data * fs,uint32_t ino)349 static int32_t get_itable_entry(struct ext2_data *fs, uint32_t ino)
350 {
351 	int rc;
352 	uint32_t ino_group = (ino - 1) / fs->sblock.s_inodes_per_group;
353 	uint32_t ino_index = (ino - 1) % fs->sblock.s_inodes_per_group;
354 
355 	LOG_DBG("ino_group:%d ino_index:%d", ino_group, ino_index);
356 
357 	rc = ext2_fetch_block_group(fs, ino_group);
358 	if (rc < 0) {
359 		return rc;
360 	}
361 
362 	uint32_t inode_size = fs->sblock.s_inode_size;
363 	uint32_t inodes_per_block = fs->block_size / inode_size;
364 
365 	uint32_t block_index  = ino_index / inodes_per_block;
366 	uint32_t block_offset = ino_index % inodes_per_block;
367 
368 	LOG_DBG("block_index:%d block_offset:%d", block_index, block_offset);
369 
370 	rc = ext2_fetch_bg_itable(&fs->bgroup, block_index);
371 	if (rc < 0) {
372 		return rc;
373 	}
374 	return block_offset;
375 }
376 
ext2_fetch_inode(struct ext2_data * fs,uint32_t ino,struct ext2_inode * inode)377 int ext2_fetch_inode(struct ext2_data *fs, uint32_t ino, struct ext2_inode *inode)
378 {
379 
380 	int32_t itable_offset = get_itable_entry(fs, ino);
381 
382 	LOG_DBG("fetch inode: %d", ino);
383 
384 	if (itable_offset < 0) {
385 		return itable_offset;
386 	}
387 
388 	struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
389 
390 	fill_inode(inode, dino);
391 
392 	/* Copy needed data into inode structure */
393 	inode->i_fs = fs;
394 	inode->flags = 0;
395 	inode->i_id = ino;
396 
397 	LOG_DBG("mode:%d size:%d links:%d", dino->i_mode, dino->i_size, dino->i_links_count);
398 	return 0;
399 }
400 
401 /*
402  * @param try_current -- if true then check if searched offset matches offset of currently fetched
403  *        block on that level. If they match then it is the block we are looking for.
404  */
fetch_level_blocks(struct ext2_inode * inode,uint32_t offsets[4],int lvl,int max_lvl,bool try_current)405 static int fetch_level_blocks(struct ext2_inode *inode, uint32_t offsets[4], int lvl, int max_lvl,
406 		bool try_current)
407 {
408 	uint32_t block;
409 	bool already_fetched = try_current && (offsets[lvl] == inode->offsets[lvl]);
410 
411 	/* all needed blocks fetched */
412 	if (lvl > max_lvl) {
413 		return 0;
414 	}
415 
416 	/* If already fetched block matches desired one we can use it and move to the next level. */
417 	if (!already_fetched) {
418 		/* Fetched block on current level was wrong.
419 		 * We can't use fetched blocks on this and next levels.
420 		 */
421 		try_current = false;
422 
423 		ext2_drop_block(inode->blocks[lvl]);
424 
425 		if (lvl == 0) {
426 			block = inode->i_block[offsets[0]];
427 		} else {
428 			uint32_t *list = (uint32_t *)inode->blocks[lvl - 1]->data;
429 
430 			block = sys_le32_to_cpu(list[offsets[lvl]]);
431 		}
432 
433 		if (block == 0) {
434 			inode->blocks[lvl] = ext2_get_empty_block(inode->i_fs);
435 		} else {
436 			inode->blocks[lvl] = ext2_get_block(inode->i_fs, block);
437 		}
438 
439 		if (inode->blocks[lvl] == NULL) {
440 			return -ENOENT;
441 		}
442 		LOG_DBG("[fetch] lvl:%d off:%d num:%d", lvl, offsets[lvl], block);
443 	}
444 	return fetch_level_blocks(inode, offsets, lvl + 1, max_lvl, try_current);
445 }
446 
ext2_fetch_inode_block(struct ext2_inode * inode,uint32_t block)447 int ext2_fetch_inode_block(struct ext2_inode *inode, uint32_t block)
448 {
449 	/* Check if correct inode block is cached. */
450 	if (inode->flags & INODE_FETCHED_BLOCK && inode->block_num == block) {
451 		return 0;
452 	}
453 
454 	LOG_DBG("inode:%d cur_blk:%d fetch_blk:%d", inode->i_id, inode->block_num, block);
455 
456 	struct ext2_data *fs = inode->i_fs;
457 	int max_lvl, ret;
458 	uint32_t offsets[MAX_OFFSETS_SIZE];
459 	bool try_current = inode->flags & INODE_FETCHED_BLOCK;
460 
461 	max_lvl = get_level_offsets(fs, block, offsets);
462 
463 	ret = fetch_level_blocks(inode, offsets, 0, max_lvl, try_current);
464 	if (ret < 0) {
465 		ext2_inode_drop_blocks(inode);
466 		return ret;
467 	}
468 
469 	memcpy(inode->offsets, offsets, MAX_OFFSETS_SIZE * sizeof(uint32_t));
470 	inode->block_lvl = max_lvl;
471 	inode->block_num = block;
472 	inode->flags |= INODE_FETCHED_BLOCK;
473 
474 	LOG_DBG("[ino:%d fetch]\t Lvl:%d {%d, %d, %d, %d}", inode->i_id, inode->block_lvl,
475 			inode->offsets[0], inode->offsets[1], inode->offsets[2], inode->offsets[3]);
476 	return 0;
477 }
478 
all_zero(const uint32_t * offsets,int lvl)479 static bool all_zero(const uint32_t *offsets, int lvl)
480 {
481 	for (int i = 0; i < lvl; ++i) {
482 		if (offsets[i] > 0) {
483 			return false;
484 		}
485 	}
486 	return true;
487 }
488 
489 /**
490  * @brief delete blocks from one described with offsets array
491  *
492  * NOTE: To use this function safely drop all fetched inode blocks
493  *
494  * @retval >=0 Number of removed blocks (only the blocks with actual inode data)
495  * @retval <0 Error
496  */
delete_blocks(struct ext2_data * fs,uint32_t block_num,int lvl,const uint32_t * offsets)497 static int64_t delete_blocks(struct ext2_data *fs, uint32_t block_num, int lvl,
498 		const uint32_t *offsets)
499 {
500 	__ASSERT(block_num != 0, "Can't delete zero block");
501 	__ASSERT(lvl >= 0 && lvl < MAX_OFFSETS_SIZE,
502 			"Expected 0 <= lvl < %d (got: lvl=%d)", lvl, MAX_OFFSETS_SIZE);
503 
504 	int ret;
505 	int64_t removed = 0, rem;
506 	uint32_t *list, start_blk;
507 	struct ext2_block *list_block = NULL;
508 	bool remove_current = false;
509 	bool block_dirty = false;
510 
511 	if (lvl == 0) {
512 		/* If we got here we will remove this block
513 		 * and it is also a block with actual inode data, hence we count it.
514 		 */
515 		remove_current = true;
516 		removed++;
517 	} else {
518 		/* Current block holds a list of blocks. */
519 		list_block = ext2_get_block(fs, block_num);
520 
521 		if (list_block == NULL) {
522 			return -ENOENT;
523 		}
524 		list = (uint32_t *)list_block->data;
525 
526 		if (all_zero(offsets, lvl)) {
527 			/* We remove all blocks that are referenced by current block, hence current
528 			 * block isn't needed anymore.
529 			 */
530 			remove_current = true;
531 			start_blk = 0;
532 
533 		} else if (lvl == 1) {
534 			/* We are on one before last layer of inode block table. The next layer are
535 			 * single blocks, hence we will just remove them.
536 			 * We can just set start_blk here and remove blocks in loop at the end of
537 			 * this function.
538 			 */
539 			start_blk = offsets[0];
540 
541 		} else {
542 			uint32_t block_num2 = sys_le32_to_cpu(list[offsets[0]]);
543 
544 			/* We don't remove all blocks referenced by current block. We have to use
545 			 * offsets to decide which part of next block we want to remove.
546 			 */
547 			if (block_num2 == 0) {
548 				LOG_ERR("Inode block that references other blocks must be nonzero");
549 				fs->flags |= EXT2_DATA_FLAGS_ERR;
550 				removed = -EINVAL;
551 				goto out;
552 			}
553 
554 			/* We will start removing whole blocks from next block on this level */
555 			start_blk = offsets[0] + 1;
556 
557 			/* Remove desired part of lower level block. */
558 			rem = delete_blocks(fs, block_num2, lvl - 1, &offsets[1]);
559 			if (rem < 0) {
560 				removed = rem;
561 				goto out;
562 			}
563 			removed += rem;
564 		}
565 
566 		/* Iterate over blocks that will be entirely deleted */
567 		for (uint32_t i = start_blk; i < fs->block_size / EXT2_BLOCK_NUM_SIZE; ++i) {
568 			uint32_t block_num2 = sys_le32_to_cpu(list[i]);
569 
570 			if (block_num2 == 0) {
571 				continue;
572 			}
573 			rem = delete_blocks(fs, block_num2, lvl - 1, zero_offsets);
574 			if (rem < 0) {
575 				removed = rem;
576 				goto out;
577 			}
578 			removed += rem;
579 			list[i] = 0;
580 			block_dirty = true;
581 		}
582 	}
583 
584 	if (remove_current) {
585 		LOG_DBG("free block %d (lvl %d)", block_num, lvl);
586 
587 		/* If we remove current block, we don't have to write it's updated content. */
588 		if (list_block) {
589 			block_dirty = false;
590 		}
591 
592 		ret = ext2_free_block(fs, block_num);
593 		if (ret < 0) {
594 			removed = ret;
595 		}
596 	}
597 out:
598 	if (removed >= 0 && list_block && block_dirty) {
599 		ret = ext2_write_block(fs, list_block);
600 		if (ret < 0) {
601 			removed = ret;
602 		}
603 	}
604 	ext2_drop_block(list_block);
605 
606 	/* On error removed will contain negative error code */
607 	return removed;
608 }
609 
get_level_offsets(struct ext2_data * fs,uint32_t block,uint32_t offsets[4])610 static int get_level_offsets(struct ext2_data *fs, uint32_t block, uint32_t offsets[4])
611 {
612 	const uint32_t B = fs->block_size / EXT2_BLOCK_NUM_SIZE;
613 	const uint32_t lvl0_blks = EXT2_INODE_BLOCK_1LVL;
614 	const uint32_t lvl1_blks = B;
615 	const uint32_t lvl2_blks = B * B;
616 	const uint32_t lvl3_blks = B * B * B;
617 
618 	/* Level 0 */
619 	if (block < lvl0_blks) {
620 		offsets[0] = block;
621 		return 0;
622 	}
623 
624 	/* Level 1 */
625 	block -= lvl0_blks;
626 	if (block < lvl1_blks) {
627 		offsets[0] = EXT2_INODE_BLOCK_1LVL;
628 		 offsets[1] = block;
629 		return 1;
630 	}
631 
632 	/* Level 2 */
633 	block -= lvl1_blks;
634 	if (block < lvl2_blks) {
635 		offsets[0] = EXT2_INODE_BLOCK_2LVL;
636 		offsets[1] = block / B;
637 		offsets[2] = block % B;
638 		return 2;
639 	}
640 
641 	/* Level 3 */
642 	if (block < lvl3_blks) {
643 		block -= lvl2_blks;
644 		offsets[0] = EXT2_INODE_BLOCK_3LVL;
645 		offsets[1] = block / (B * B);
646 		offsets[2] = (block % (B * B)) / B;
647 		offsets[3] = (block % (B * B)) % B;
648 		return 3;
649 	}
650 	/* Block number is too large */
651 	return -EINVAL;
652 }
653 
block0_level(uint32_t block)654 static int block0_level(uint32_t block)
655 {
656 	if (block >= EXT2_INODE_BLOCK_1LVL) {
657 		return block - EXT2_INODE_BLOCK_1LVL + 1;
658 	}
659 	return 0;
660 }
661 
ext2_inode_remove_blocks(struct ext2_inode * inode,uint32_t first)662 int64_t ext2_inode_remove_blocks(struct ext2_inode *inode, uint32_t first)
663 {
664 	uint32_t start;
665 	int max_lvl;
666 	int64_t ret, removed = 0;
667 	uint32_t offsets[4];
668 	struct ext2_data *fs = inode->i_fs;
669 
670 	max_lvl = get_level_offsets(inode->i_fs, first, offsets);
671 
672 	if (max_lvl < 0) {
673 		return max_lvl;
674 	}
675 
676 	if (all_zero(&offsets[1], max_lvl)) {
677 		/* The first block to remove is either:
678 		 *  - one of the first 12 blocks in the indode
679 		 *  - the first referenced block in the indirect block list;
680 		 *    we remove also the indirect block
681 		 */
682 		start = offsets[0];
683 	} else {
684 		/* There will be some blocks referenced from first affected block hence we can't
685 		 * remove it.
686 		 */
687 		if (inode->i_block[offsets[0]] == 0) {
688 			LOG_ERR("Inode block that references other blocks must be nonzero");
689 			fs->flags |= EXT2_DATA_FLAGS_ERR;
690 			return -EINVAL;
691 		}
692 
693 		start = offsets[0] + 1;
694 		ret = delete_blocks(inode->i_fs, inode->i_block[offsets[0]],
695 				block0_level(offsets[0]), &offsets[1]);
696 		if (ret < 0) {
697 			return ret;
698 		}
699 		removed += ret;
700 	}
701 
702 	for (uint32_t i = start; i < EXT2_INODE_BLOCKS; i++) {
703 		if (inode->i_block[i] == 0) {
704 			continue;
705 		}
706 		ret = delete_blocks(inode->i_fs, inode->i_block[i], block0_level(i),
707 				zero_offsets);
708 		if (ret < 0) {
709 			return ret;
710 		}
711 		removed += ret;
712 		inode->i_block[i] = 0;
713 	}
714 	return removed;
715 }
alloc_level_blocks(struct ext2_inode * inode)716 static int alloc_level_blocks(struct ext2_inode *inode)
717 {
718 	int ret = 0;
719 	uint32_t *block;
720 	bool allocated = false;
721 	struct ext2_data *fs = inode->i_fs;
722 
723 	for (int lvl = 0; lvl <= inode->block_lvl; ++lvl) {
724 		if (lvl == 0) {
725 			block = &inode->i_block[inode->offsets[lvl]];
726 		} else {
727 			block = &((uint32_t *)inode->blocks[lvl - 1]->data)[inode->offsets[lvl]];
728 			*block = sys_le32_to_cpu(*block);
729 		}
730 
731 		if (*block == 0) {
732 			ret = ext2_assign_block_num(fs, inode->blocks[lvl]);
733 			if (ret < 0) {
734 				return ret;
735 			}
736 
737 			/* Update block from higher level. */
738 			*block = sys_cpu_to_le32(inode->blocks[lvl]->num);
739 			if (lvl > 0) {
740 				ret = ext2_write_block(fs, inode->blocks[lvl-1]);
741 				if (ret < 0) {
742 					return ret;
743 				}
744 			}
745 			allocated = true;
746 			/* Allocating block on that level implies that blocks on lower levels will
747 			 * be allocated too hence we can set allocated here.
748 			 */
749 			LOG_DBG("Alloc lvl:%d (num: %d) %s", lvl, *block,
750 					lvl == inode->block_lvl ? "data" : "indirect");
751 		}
752 	}
753 	if (allocated) {
754 		/* Update number of reserved blocks.
755 		 * (We are always counting 512 size blocks.)
756 		 */
757 		inode->i_blocks += fs->block_size / 512;
758 		ret = ext2_commit_inode(inode);
759 	}
760 	return ret;
761 }
762 
ext2_commit_superblock(struct ext2_data * fs)763 int ext2_commit_superblock(struct ext2_data *fs)
764 {
765 	int ret;
766 	struct ext2_block *b;
767 	uint32_t sblock_offset;
768 
769 	if (fs->block_size == 1024) {
770 		sblock_offset = 0;
771 		b = ext2_get_block(fs, 1);
772 	} else {
773 		sblock_offset = 1024;
774 		b = ext2_get_block(fs, 0);
775 	}
776 	if (b == NULL) {
777 		return -ENOENT;
778 	}
779 
780 	struct ext2_disk_superblock *disk_sb =
781 		(struct ext2_disk_superblock *)(b->data + sblock_offset);
782 
783 	fill_disk_sblock(disk_sb, &fs->sblock);
784 
785 	ret = ext2_write_block(fs, b);
786 	if (ret < 0) {
787 		return ret;
788 	}
789 	ext2_drop_block(b);
790 	return 0;
791 }
792 
ext2_commit_bg(struct ext2_data * fs)793 int ext2_commit_bg(struct ext2_data *fs)
794 {
795 	int ret;
796 	struct ext2_bgroup *bg = &fs->bgroup;
797 
798 	uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
799 	uint32_t block = bg->num / groups_per_block;
800 	uint32_t offset = bg->num % groups_per_block;
801 	uint32_t global_block = fs->sblock.s_first_data_block + 1 + block;
802 
803 	struct ext2_block *b = ext2_get_block(fs, global_block);
804 
805 	if (b == NULL) {
806 		return -ENOENT;
807 	}
808 
809 	struct ext2_disk_bgroup *disk_bg = ((struct ext2_disk_bgroup *)b->data) + offset;
810 
811 	fill_disk_bgroup(disk_bg, bg);
812 
813 	ret = ext2_write_block(fs, b);
814 	if (ret < 0) {
815 		return ret;
816 	}
817 	ext2_drop_block(b);
818 	return 0;
819 }
820 
ext2_commit_inode(struct ext2_inode * inode)821 int ext2_commit_inode(struct ext2_inode *inode)
822 {
823 	struct ext2_data *fs = inode->i_fs;
824 
825 	int32_t itable_offset = get_itable_entry(fs, inode->i_id);
826 
827 	if (itable_offset < 0) {
828 		return itable_offset;
829 	}
830 
831 	/* get pointer to proper inode in fetched block */
832 	struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
833 
834 	/* fill dinode */
835 	fill_disk_inode(dino, inode);
836 
837 	return ext2_write_block(fs, fs->bgroup.inode_table);
838 }
839 
ext2_commit_inode_block(struct ext2_inode * inode)840 int ext2_commit_inode_block(struct ext2_inode *inode)
841 {
842 	if (!(inode->flags & INODE_FETCHED_BLOCK)) {
843 		return -EINVAL;
844 	}
845 
846 	int ret;
847 
848 	LOG_DBG("inode:%d current_blk:%d", inode->i_id, inode->block_num);
849 
850 	ret = alloc_level_blocks(inode);
851 	if (ret < 0) {
852 		return ret;
853 	}
854 	ret = ext2_write_block(inode->i_fs, inode_current_block(inode));
855 	return ret;
856 }
857 
ext2_clear_inode(struct ext2_data * fs,uint32_t ino)858 int ext2_clear_inode(struct ext2_data *fs, uint32_t ino)
859 {
860 	int ret;
861 	int32_t itable_offset = get_itable_entry(fs, ino);
862 
863 	if (itable_offset < 0) {
864 		return itable_offset;
865 	}
866 
867 	memset(&BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset], 0, sizeof(struct ext2_disk_inode));
868 	ret = ext2_write_block(fs, fs->bgroup.inode_table);
869 	return ret;
870 }
871 
ext2_alloc_block(struct ext2_data * fs)872 int64_t ext2_alloc_block(struct ext2_data *fs)
873 {
874 	int rc, bitmap_slot;
875 	uint32_t group = 0, set;
876 	int32_t total;
877 
878 	rc = ext2_fetch_block_group(fs, group);
879 	if (rc < 0) {
880 		return rc;
881 	}
882 
883 	LOG_DBG("Free blocks: %d", fs->bgroup.bg_free_blocks_count);
884 	while ((rc >= 0) && (fs->bgroup.bg_free_blocks_count == 0)) {
885 		group++;
886 		rc = ext2_fetch_block_group(fs, group);
887 		if (rc == -ERANGE) {
888 			/* reached last group */
889 			return -ENOSPC;
890 		}
891 	}
892 	if (rc < 0) {
893 		return rc;
894 	}
895 
896 	rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
897 	if (rc < 0) {
898 		return rc;
899 	}
900 
901 	bitmap_slot = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->block_size);
902 	if (bitmap_slot < 0) {
903 		LOG_WRN("Cannot find free block in group %d (rc: %d)", group, bitmap_slot);
904 		return bitmap_slot;
905 	}
906 
907 	/* In bitmap blocks are counted from s_first_data_block hence we have to add this offset. */
908 	total = group * fs->sblock.s_blocks_per_group + bitmap_slot + fs->sblock.s_first_data_block;
909 
910 	LOG_DBG("Found free block %d in group %d (total: %d)", bitmap_slot, group, total);
911 
912 	rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), bitmap_slot, fs->block_size);
913 	if (rc < 0) {
914 		return rc;
915 	}
916 
917 	fs->bgroup.bg_free_blocks_count -= 1;
918 	fs->sblock.s_free_blocks_count -= 1;
919 
920 	set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
921 
922 	if (set != (fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count)) {
923 		error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
924 		return -EINVAL;
925 	}
926 
927 	rc = ext2_commit_superblock(fs);
928 	if (rc < 0) {
929 		LOG_DBG("super block write returned: %d", rc);
930 		return -EIO;
931 	}
932 	rc = ext2_commit_bg(fs);
933 	if (rc < 0) {
934 		LOG_DBG("block group write returned: %d", rc);
935 		return -EIO;
936 	}
937 	rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
938 	if (rc < 0) {
939 		LOG_DBG("block bitmap write returned: %d", rc);
940 		return -EIO;
941 	}
942 	return total;
943 }
944 
check_zero_inode(struct ext2_data * fs,uint32_t ino)945 static int check_zero_inode(struct ext2_data *fs, uint32_t ino)
946 {
947 	int32_t itable_offset = get_itable_entry(fs, ino);
948 
949 	if (itable_offset < 0) {
950 		return itable_offset;
951 	}
952 
953 	uint8_t *bytes = (uint8_t *)&BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
954 
955 	for (int i = 0; i < sizeof(struct ext2_disk_inode); ++i) {
956 		if (bytes[i] != 0) {
957 			return -EINVAL;
958 		}
959 	}
960 	return 0;
961 }
962 
ext2_alloc_inode(struct ext2_data * fs)963 int32_t ext2_alloc_inode(struct ext2_data *fs)
964 {
965 	int rc, r;
966 	uint32_t group = 0, set;
967 	int32_t global_idx;
968 
969 	rc = ext2_fetch_block_group(fs, group);
970 
971 	while (fs->bgroup.bg_free_inodes_count == 0 && rc >= 0) {
972 		group++;
973 		rc = ext2_fetch_block_group(fs, group);
974 		if (rc == -ERANGE) {
975 			/* reached last group */
976 			return -ENOSPC;
977 		}
978 	}
979 
980 	if (rc < 0) {
981 		return rc;
982 	}
983 
984 	LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
985 	LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
986 
987 	rc = ext2_fetch_bg_ibitmap(&fs->bgroup);
988 	if (rc < 0) {
989 		return rc;
990 	}
991 
992 	r = ext2_bitmap_find_free(BGROUP_INODE_BITMAP(&fs->bgroup), fs->block_size);
993 	if (r < 0) {
994 		LOG_DBG("Cannot find free inode in group %d (rc: %d)", group, r);
995 		return r;
996 	}
997 
998 	/* Add 1 because inodes are counted from 1 not 0. */
999 	global_idx = group * fs->sblock.s_inodes_per_group + r + 1;
1000 
1001 	/* Inode table entry for found inode must be cleared. */
1002 	if (check_zero_inode(fs, global_idx) != 0) {
1003 		error_behavior(fs,  "Inode is not cleared in inode table!");
1004 		return -EINVAL;
1005 	}
1006 
1007 	LOG_DBG("Found free inode %d in group %d (global_idx: %d)", r, group, global_idx);
1008 
1009 	rc = ext2_bitmap_set(BGROUP_INODE_BITMAP(&fs->bgroup), r, fs->block_size);
1010 	if (rc < 0) {
1011 		return rc;
1012 	}
1013 
1014 	fs->bgroup.bg_free_inodes_count -= 1;
1015 	fs->sblock.s_free_inodes_count -= 1;
1016 
1017 	set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
1018 
1019 	if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
1020 		error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
1021 		return -EINVAL;
1022 	}
1023 
1024 	rc = ext2_commit_superblock(fs);
1025 	if (rc < 0) {
1026 		LOG_DBG("super block write returned: %d", rc);
1027 		return -EIO;
1028 	}
1029 	rc = ext2_commit_bg(fs);
1030 	if (rc < 0) {
1031 		LOG_DBG("block group write returned: %d", rc);
1032 		return -EIO;
1033 	}
1034 	rc = ext2_write_block(fs, fs->bgroup.inode_bitmap);
1035 	if (rc < 0) {
1036 		LOG_DBG("block bitmap write returned: %d", rc);
1037 		return -EIO;
1038 	}
1039 
1040 	LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
1041 	LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
1042 
1043 	return global_idx;
1044 }
1045 
ext2_free_block(struct ext2_data * fs,uint32_t block)1046 int ext2_free_block(struct ext2_data *fs, uint32_t block)
1047 {
1048 	LOG_DBG("Free block %d", block);
1049 
1050 	/* Block bitmaps tracks blocks starting from s_first_data_block. */
1051 	block -= fs->sblock.s_first_data_block;
1052 
1053 	int rc;
1054 	uint32_t group = block / fs->sblock.s_blocks_per_group;
1055 	uint32_t off = block % fs->sblock.s_blocks_per_group;
1056 	uint32_t set;
1057 
1058 	rc = ext2_fetch_block_group(fs, group);
1059 	if (rc < 0) {
1060 		return rc;
1061 	}
1062 
1063 	rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
1064 	if (rc < 0) {
1065 		return rc;
1066 	}
1067 
1068 	rc = ext2_bitmap_unset(BGROUP_BLOCK_BITMAP(&fs->bgroup), off, fs->block_size);
1069 	if (rc < 0) {
1070 		return rc;
1071 	}
1072 
1073 	fs->bgroup.bg_free_blocks_count += 1;
1074 	fs->sblock.s_free_blocks_count += 1;
1075 
1076 	set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
1077 
1078 	if (set != fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count) {
1079 		error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
1080 		return -EINVAL;
1081 	}
1082 
1083 	rc = ext2_commit_superblock(fs);
1084 	if (rc < 0) {
1085 		LOG_DBG("super block write returned: %d", rc);
1086 		return -EIO;
1087 	}
1088 	rc = ext2_commit_bg(fs);
1089 	if (rc < 0) {
1090 		LOG_DBG("block group write returned: %d", rc);
1091 		return -EIO;
1092 	}
1093 	rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
1094 	if (rc < 0) {
1095 		LOG_DBG("block bitmap write returned: %d", rc);
1096 		return -EIO;
1097 	}
1098 	return 0;
1099 }
1100 
ext2_free_inode(struct ext2_data * fs,uint32_t ino,bool directory)1101 int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory)
1102 {
1103 	LOG_DBG("Free inode %d", ino);
1104 
1105 	int rc;
1106 	uint32_t group = (ino - 1) / fs->sblock.s_inodes_per_group;
1107 	uint32_t bitmap_off = (ino - 1) % fs->sblock.s_inodes_per_group;
1108 	uint32_t set;
1109 
1110 	rc = ext2_fetch_block_group(fs, group);
1111 	if (rc < 0) {
1112 		return rc;
1113 	}
1114 
1115 	rc = ext2_fetch_bg_ibitmap(&fs->bgroup);
1116 	if (rc < 0) {
1117 		return rc;
1118 	}
1119 
1120 	rc = ext2_bitmap_unset(BGROUP_INODE_BITMAP(&fs->bgroup), bitmap_off, fs->block_size);
1121 	if (rc < 0) {
1122 		return rc;
1123 	}
1124 
1125 	rc = ext2_clear_inode(fs, ino);
1126 	if (rc < 0) {
1127 		return rc;
1128 	}
1129 
1130 	fs->bgroup.bg_free_inodes_count += 1;
1131 	fs->sblock.s_free_inodes_count += 1;
1132 
1133 	if (directory) {
1134 		fs->bgroup.bg_used_dirs_count -= 1;
1135 	}
1136 
1137 	set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
1138 
1139 	if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
1140 		error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
1141 		return -EINVAL;
1142 	}
1143 
1144 	LOG_INF("Inode %d is free", ino);
1145 
1146 	rc = ext2_commit_superblock(fs);
1147 	if (rc < 0) {
1148 		LOG_DBG("super block write returned: %d", rc);
1149 		return -EIO;
1150 	}
1151 	rc = ext2_commit_bg(fs);
1152 	if (rc < 0) {
1153 		LOG_DBG("block group write returned: %d", rc);
1154 		return -EIO;
1155 	}
1156 	rc = ext2_write_block(fs, fs->bgroup.inode_bitmap);
1157 	if (rc < 0) {
1158 		LOG_DBG("block bitmap write returned: %d", rc);
1159 		return -EIO;
1160 	}
1161 	rc = fs->backend_ops->sync(fs);
1162 	if (rc < 0) {
1163 		return -EIO;
1164 	}
1165 	return 0;
1166 }
1167