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