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