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 (all_zero(&offsets[1], max_lvl)) {
673 		/* The first block to remove is either:
674 		 *  - one of the first 12 blocks in the indode
675 		 *  - the first referenced block in the indirect block list;
676 		 *    we remove also the indirect block
677 		 */
678 		start = offsets[0];
679 	} else {
680 		/* There will be some blocks referenced from first affected block hence we can't
681 		 * remove it.
682 		 */
683 		if (inode->i_block[offsets[0]] == 0) {
684 			LOG_ERR("Inode block that references other blocks must be nonzero");
685 			fs->flags |= EXT2_DATA_FLAGS_ERR;
686 			return -EINVAL;
687 		}
688 
689 		start = offsets[0] + 1;
690 		ret = delete_blocks(inode->i_fs, inode->i_block[offsets[0]],
691 				block0_level(offsets[0]), &offsets[1]);
692 		if (ret < 0) {
693 			return ret;
694 		}
695 		removed += ret;
696 	}
697 
698 	for (uint32_t i = start; i < EXT2_INODE_BLOCKS; i++) {
699 		if (inode->i_block[i] == 0) {
700 			continue;
701 		}
702 		ret = delete_blocks(inode->i_fs, inode->i_block[i], block0_level(i),
703 				zero_offsets);
704 		if (ret < 0) {
705 			return ret;
706 		}
707 		removed += ret;
708 		inode->i_block[i] = 0;
709 	}
710 	return removed;
711 }
alloc_level_blocks(struct ext2_inode * inode)712 static int alloc_level_blocks(struct ext2_inode *inode)
713 {
714 	int ret = 0;
715 	uint32_t *block;
716 	bool allocated = false;
717 	struct ext2_data *fs = inode->i_fs;
718 
719 	for (int lvl = 0; lvl <= inode->block_lvl; ++lvl) {
720 		if (lvl == 0) {
721 			block = &inode->i_block[inode->offsets[lvl]];
722 		} else {
723 			block = &((uint32_t *)inode->blocks[lvl - 1]->data)[inode->offsets[lvl]];
724 			*block = sys_le32_to_cpu(*block);
725 		}
726 
727 		if (*block == 0) {
728 			ret = ext2_assign_block_num(fs, inode->blocks[lvl]);
729 			if (ret < 0) {
730 				return ret;
731 			}
732 
733 			/* Update block from higher level. */
734 			*block = sys_cpu_to_le32(inode->blocks[lvl]->num);
735 			if (lvl > 0) {
736 				ret = ext2_write_block(fs, inode->blocks[lvl-1]);
737 				if (ret < 0) {
738 					return ret;
739 				}
740 			}
741 			allocated = true;
742 			/* Allocating block on that level implies that blocks on lower levels will
743 			 * be allocated too hence we can set allocated here.
744 			 */
745 			LOG_DBG("Alloc lvl:%d (num: %d) %s", lvl, *block,
746 					lvl == inode->block_lvl ? "data" : "indirect");
747 		}
748 	}
749 	if (allocated) {
750 		/* Update number of reserved blocks.
751 		 * (We are always counting 512 size blocks.)
752 		 */
753 		inode->i_blocks += fs->block_size / 512;
754 		ret = ext2_commit_inode(inode);
755 	}
756 	return ret;
757 }
758 
ext2_commit_superblock(struct ext2_data * fs)759 int ext2_commit_superblock(struct ext2_data *fs)
760 {
761 	int ret;
762 	struct ext2_block *b;
763 	uint32_t sblock_offset;
764 
765 	if (fs->block_size == 1024) {
766 		sblock_offset = 0;
767 		b = ext2_get_block(fs, 1);
768 	} else {
769 		sblock_offset = 1024;
770 		b = ext2_get_block(fs, 0);
771 	}
772 	if (b == NULL) {
773 		return -ENOENT;
774 	}
775 
776 	struct ext2_disk_superblock *disk_sb =
777 		(struct ext2_disk_superblock *)(b->data + sblock_offset);
778 
779 	fill_disk_sblock(disk_sb, &fs->sblock);
780 
781 	ret = ext2_write_block(fs, b);
782 	if (ret < 0) {
783 		return ret;
784 	}
785 	ext2_drop_block(b);
786 	return 0;
787 }
788 
ext2_commit_bg(struct ext2_data * fs)789 int ext2_commit_bg(struct ext2_data *fs)
790 {
791 	int ret;
792 	struct ext2_bgroup *bg = &fs->bgroup;
793 
794 	uint32_t groups_per_block = fs->block_size / sizeof(struct ext2_disk_bgroup);
795 	uint32_t block = bg->num / groups_per_block;
796 	uint32_t offset = bg->num % groups_per_block;
797 	uint32_t global_block = fs->sblock.s_first_data_block + 1 + block;
798 
799 	struct ext2_block *b = ext2_get_block(fs, global_block);
800 
801 	if (b == NULL) {
802 		return -ENOENT;
803 	}
804 
805 	struct ext2_disk_bgroup *disk_bg = ((struct ext2_disk_bgroup *)b->data) + offset;
806 
807 	fill_disk_bgroup(disk_bg, bg);
808 
809 	ret = ext2_write_block(fs, b);
810 	if (ret < 0) {
811 		return ret;
812 	}
813 	ext2_drop_block(b);
814 	return 0;
815 }
816 
ext2_commit_inode(struct ext2_inode * inode)817 int ext2_commit_inode(struct ext2_inode *inode)
818 {
819 	struct ext2_data *fs = inode->i_fs;
820 
821 	int32_t itable_offset = get_itable_entry(fs, inode->i_id);
822 
823 	if (itable_offset < 0) {
824 		return itable_offset;
825 	}
826 
827 	/* get pointer to proper inode in fetched block */
828 	struct ext2_disk_inode *dino = &BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
829 
830 	/* fill dinode */
831 	fill_disk_inode(dino, inode);
832 
833 	return ext2_write_block(fs, fs->bgroup.inode_table);
834 }
835 
ext2_commit_inode_block(struct ext2_inode * inode)836 int ext2_commit_inode_block(struct ext2_inode *inode)
837 {
838 	if (!(inode->flags & INODE_FETCHED_BLOCK)) {
839 		return -EINVAL;
840 	}
841 
842 	int ret;
843 
844 	LOG_DBG("inode:%d current_blk:%d", inode->i_id, inode->block_num);
845 
846 	ret = alloc_level_blocks(inode);
847 	if (ret < 0) {
848 		return ret;
849 	}
850 	ret = ext2_write_block(inode->i_fs, inode_current_block(inode));
851 	return ret;
852 }
853 
ext2_clear_inode(struct ext2_data * fs,uint32_t ino)854 int ext2_clear_inode(struct ext2_data *fs, uint32_t ino)
855 {
856 	int ret;
857 	int32_t itable_offset = get_itable_entry(fs, ino);
858 
859 	if (itable_offset < 0) {
860 		return itable_offset;
861 	}
862 
863 	memset(&BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset], 0, sizeof(struct ext2_disk_inode));
864 	ret = ext2_write_block(fs, fs->bgroup.inode_table);
865 	return ret;
866 }
867 
ext2_alloc_block(struct ext2_data * fs)868 int64_t ext2_alloc_block(struct ext2_data *fs)
869 {
870 	int rc, bitmap_slot;
871 	uint32_t group = 0, set;
872 	int32_t total;
873 
874 	rc = ext2_fetch_block_group(fs, group);
875 	if (rc < 0) {
876 		return rc;
877 	}
878 
879 	LOG_DBG("Free blocks: %d", fs->bgroup.bg_free_blocks_count);
880 	while ((rc >= 0) && (fs->bgroup.bg_free_blocks_count == 0)) {
881 		group++;
882 		rc = ext2_fetch_block_group(fs, group);
883 		if (rc == -ERANGE) {
884 			/* reached last group */
885 			return -ENOSPC;
886 		}
887 	}
888 	if (rc < 0) {
889 		return rc;
890 	}
891 
892 	rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
893 	if (rc < 0) {
894 		return rc;
895 	}
896 
897 	bitmap_slot = ext2_bitmap_find_free(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->block_size);
898 	if (bitmap_slot < 0) {
899 		LOG_WRN("Cannot find free block in group %d (rc: %d)", group, bitmap_slot);
900 		return bitmap_slot;
901 	}
902 
903 	/* In bitmap blocks are counted from s_first_data_block hence we have to add this offset. */
904 	total = group * fs->sblock.s_blocks_per_group + bitmap_slot + fs->sblock.s_first_data_block;
905 
906 	LOG_DBG("Found free block %d in group %d (total: %d)", bitmap_slot, group, total);
907 
908 	rc = ext2_bitmap_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), bitmap_slot, fs->block_size);
909 	if (rc < 0) {
910 		return rc;
911 	}
912 
913 	fs->bgroup.bg_free_blocks_count -= 1;
914 	fs->sblock.s_free_blocks_count -= 1;
915 
916 	set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
917 
918 	if (set != (fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count)) {
919 		error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
920 		return -EINVAL;
921 	}
922 
923 	rc = ext2_commit_superblock(fs);
924 	if (rc < 0) {
925 		LOG_DBG("super block write returned: %d", rc);
926 		return -EIO;
927 	}
928 	rc = ext2_commit_bg(fs);
929 	if (rc < 0) {
930 		LOG_DBG("block group write returned: %d", rc);
931 		return -EIO;
932 	}
933 	rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
934 	if (rc < 0) {
935 		LOG_DBG("block bitmap write returned: %d", rc);
936 		return -EIO;
937 	}
938 	return total;
939 }
940 
check_zero_inode(struct ext2_data * fs,uint32_t ino)941 static int check_zero_inode(struct ext2_data *fs, uint32_t ino)
942 {
943 	int32_t itable_offset = get_itable_entry(fs, ino);
944 
945 	if (itable_offset < 0) {
946 		return itable_offset;
947 	}
948 
949 	uint8_t *bytes = (uint8_t *)&BGROUP_INODE_TABLE(&fs->bgroup)[itable_offset];
950 
951 	for (int i = 0; i < sizeof(struct ext2_disk_inode); ++i) {
952 		if (bytes[i] != 0) {
953 			return -EINVAL;
954 		}
955 	}
956 	return 0;
957 }
958 
ext2_alloc_inode(struct ext2_data * fs)959 int32_t ext2_alloc_inode(struct ext2_data *fs)
960 {
961 	int rc, r;
962 	uint32_t group = 0, set;
963 	int32_t global_idx;
964 
965 	rc = ext2_fetch_block_group(fs, group);
966 
967 	while (fs->bgroup.bg_free_inodes_count == 0 && rc >= 0) {
968 		group++;
969 		rc = ext2_fetch_block_group(fs, group);
970 		if (rc == -ERANGE) {
971 			/* reached last group */
972 			return -ENOSPC;
973 		}
974 	}
975 
976 	if (rc < 0) {
977 		return rc;
978 	}
979 
980 	LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
981 	LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
982 
983 	rc = ext2_fetch_bg_ibitmap(&fs->bgroup);
984 	if (rc < 0) {
985 		return rc;
986 	}
987 
988 	r = ext2_bitmap_find_free(BGROUP_INODE_BITMAP(&fs->bgroup), fs->block_size);
989 	if (r < 0) {
990 		LOG_DBG("Cannot find free inode in group %d (rc: %d)", group, r);
991 		return r;
992 	}
993 
994 	/* Add 1 because inodes are counted from 1 not 0. */
995 	global_idx = group * fs->sblock.s_inodes_per_group + r + 1;
996 
997 	/* Inode table entry for found inode must be cleared. */
998 	if (check_zero_inode(fs, global_idx) != 0) {
999 		error_behavior(fs,  "Inode is not cleared in inode table!");
1000 		return -EINVAL;
1001 	}
1002 
1003 	LOG_DBG("Found free inode %d in group %d (global_idx: %d)", r, group, global_idx);
1004 
1005 	rc = ext2_bitmap_set(BGROUP_INODE_BITMAP(&fs->bgroup), r, fs->block_size);
1006 	if (rc < 0) {
1007 		return rc;
1008 	}
1009 
1010 	fs->bgroup.bg_free_inodes_count -= 1;
1011 	fs->sblock.s_free_inodes_count -= 1;
1012 
1013 	set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
1014 
1015 	if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
1016 		error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
1017 		return -EINVAL;
1018 	}
1019 
1020 	rc = ext2_commit_superblock(fs);
1021 	if (rc < 0) {
1022 		LOG_DBG("super block write returned: %d", rc);
1023 		return -EIO;
1024 	}
1025 	rc = ext2_commit_bg(fs);
1026 	if (rc < 0) {
1027 		LOG_DBG("block group write returned: %d", rc);
1028 		return -EIO;
1029 	}
1030 	rc = ext2_write_block(fs, fs->bgroup.inode_bitmap);
1031 	if (rc < 0) {
1032 		LOG_DBG("block bitmap write returned: %d", rc);
1033 		return -EIO;
1034 	}
1035 
1036 	LOG_DBG("Free inodes (bg): %d", fs->bgroup.bg_free_inodes_count);
1037 	LOG_DBG("Free inodes (sb): %d", fs->sblock.s_free_inodes_count);
1038 
1039 	return global_idx;
1040 }
1041 
ext2_free_block(struct ext2_data * fs,uint32_t block)1042 int ext2_free_block(struct ext2_data *fs, uint32_t block)
1043 {
1044 	LOG_DBG("Free block %d", block);
1045 
1046 	/* Block bitmaps tracks blocks starting from s_first_data_block. */
1047 	block -= fs->sblock.s_first_data_block;
1048 
1049 	int rc;
1050 	uint32_t group = block / fs->sblock.s_blocks_per_group;
1051 	uint32_t off = block % fs->sblock.s_blocks_per_group;
1052 	uint32_t set;
1053 
1054 	rc = ext2_fetch_block_group(fs, group);
1055 	if (rc < 0) {
1056 		return rc;
1057 	}
1058 
1059 	rc = ext2_fetch_bg_bbitmap(&fs->bgroup);
1060 	if (rc < 0) {
1061 		return rc;
1062 	}
1063 
1064 	rc = ext2_bitmap_unset(BGROUP_BLOCK_BITMAP(&fs->bgroup), off, fs->block_size);
1065 	if (rc < 0) {
1066 		return rc;
1067 	}
1068 
1069 	fs->bgroup.bg_free_blocks_count += 1;
1070 	fs->sblock.s_free_blocks_count += 1;
1071 
1072 	set = ext2_bitmap_count_set(BGROUP_BLOCK_BITMAP(&fs->bgroup), fs->sblock.s_blocks_count);
1073 
1074 	if (set != fs->sblock.s_blocks_count - fs->sblock.s_free_blocks_count) {
1075 		error_behavior(fs, "Wrong number of used blocks in superblock and bitmap");
1076 		return -EINVAL;
1077 	}
1078 
1079 	rc = ext2_commit_superblock(fs);
1080 	if (rc < 0) {
1081 		LOG_DBG("super block write returned: %d", rc);
1082 		return -EIO;
1083 	}
1084 	rc = ext2_commit_bg(fs);
1085 	if (rc < 0) {
1086 		LOG_DBG("block group write returned: %d", rc);
1087 		return -EIO;
1088 	}
1089 	rc = ext2_write_block(fs, fs->bgroup.block_bitmap);
1090 	if (rc < 0) {
1091 		LOG_DBG("block bitmap write returned: %d", rc);
1092 		return -EIO;
1093 	}
1094 	return 0;
1095 }
1096 
ext2_free_inode(struct ext2_data * fs,uint32_t ino,bool directory)1097 int ext2_free_inode(struct ext2_data *fs, uint32_t ino, bool directory)
1098 {
1099 	LOG_DBG("Free inode %d", ino);
1100 
1101 	int rc;
1102 	uint32_t group = (ino - 1) / fs->sblock.s_inodes_per_group;
1103 	uint32_t bitmap_off = (ino - 1) % fs->sblock.s_inodes_per_group;
1104 	uint32_t set;
1105 
1106 	rc = ext2_fetch_block_group(fs, group);
1107 	if (rc < 0) {
1108 		return rc;
1109 	}
1110 
1111 	rc = ext2_fetch_bg_ibitmap(&fs->bgroup);
1112 	if (rc < 0) {
1113 		return rc;
1114 	}
1115 
1116 	rc = ext2_bitmap_unset(BGROUP_INODE_BITMAP(&fs->bgroup), bitmap_off, fs->block_size);
1117 	if (rc < 0) {
1118 		return rc;
1119 	}
1120 
1121 	rc = ext2_clear_inode(fs, ino);
1122 	if (rc < 0) {
1123 		return rc;
1124 	}
1125 
1126 	fs->bgroup.bg_free_inodes_count += 1;
1127 	fs->sblock.s_free_inodes_count += 1;
1128 
1129 	if (directory) {
1130 		fs->bgroup.bg_used_dirs_count -= 1;
1131 	}
1132 
1133 	set = ext2_bitmap_count_set(BGROUP_INODE_BITMAP(&fs->bgroup), fs->sblock.s_inodes_count);
1134 
1135 	if (set != fs->sblock.s_inodes_count - fs->sblock.s_free_inodes_count) {
1136 		error_behavior(fs, "Wrong number of used inodes in superblock and bitmap");
1137 		return -EINVAL;
1138 	}
1139 
1140 	LOG_INF("Inode %d is free", ino);
1141 
1142 	rc = ext2_commit_superblock(fs);
1143 	if (rc < 0) {
1144 		LOG_DBG("super block write returned: %d", rc);
1145 		return -EIO;
1146 	}
1147 	rc = ext2_commit_bg(fs);
1148 	if (rc < 0) {
1149 		LOG_DBG("block group write returned: %d", rc);
1150 		return -EIO;
1151 	}
1152 	rc = ext2_write_block(fs, fs->bgroup.inode_bitmap);
1153 	if (rc < 0) {
1154 		LOG_DBG("block bitmap write returned: %d", rc);
1155 		return -EIO;
1156 	}
1157 	rc = fs->backend_ops->sync(fs);
1158 	if (rc < 0) {
1159 		return -EIO;
1160 	}
1161 	return 0;
1162 }
1163