1#include <block/bcache.h>
2#include <console/printf.h>
3#include <fs/ext2.h>
4#include <stdbool.h>
5#include <stdint.h>
6
7static bool find_free_bit(uint8_t *bitmap, uint32_t size, uint32_t *byte_pos,
8 uint32_t *bit_pos) {
9 for (*byte_pos = 0; *byte_pos < size; ++(*byte_pos)) {
10 if (bitmap[*byte_pos] != 0xFF) {
11 for (*bit_pos = 0; *bit_pos < 8; ++(*bit_pos)) {
12 if (!BIT_TEST(bitmap[*byte_pos], *bit_pos)) {
13 return true;
14 }
15 }
16 }
17 }
18 return false;
19}
20
21static uint32_t alloc_from_bitmap(struct ext2_fs *fs, uint32_t bitmap_block,
22 uint32_t items_per_group, uint32_t group,
23 void (*update_counts)(struct ext2_fs *,
24 uint32_t)) {
25 uint32_t byte_pos, bit_pos;
26
27 struct bcache_entry *ent;
28 uint8_t *bitmap = ext2_block_read(fs, block_num: bitmap_block, out: &ent);
29 if (!bitmap)
30 return -1;
31
32 if (!find_free_bit(bitmap, size: fs->block_size, byte_pos: &byte_pos, bit_pos: &bit_pos)) {
33 bcache_ent_release(ent);
34 return -1;
35 }
36
37 bitmap[byte_pos] = BIT_SET(bitmap[byte_pos], bit_pos);
38 bcache_ent_release(ent);
39
40 if (!ext2_block_write(fs, ent, EXT2_PRIO_BITMAPS))
41 return -1;
42
43 update_counts(fs, group);
44 return group * items_per_group + (byte_pos * 8 + bit_pos);
45}
46
47static void update_block_counts(struct ext2_fs *fs, uint32_t group) {
48
49 enum irql irql = ext2_fs_lock(fs);
50 fs->group_desc[group].free_blocks_count--;
51 fs->sblock->free_blocks_count--;
52 ext2_fs_unlock(fs, i: irql);
53
54 ext2_write_group_desc(fs);
55 ext2_write_superblock(fs);
56}
57
58static void update_inode_counts(struct ext2_fs *fs, uint32_t group) {
59 fs->group_desc[group].free_inodes_count--;
60 fs->sblock->free_inodes_count--;
61 ext2_write_group_desc(fs);
62 ext2_write_superblock(fs);
63}
64
65uint32_t ext2_alloc_block(struct ext2_fs *fs) {
66 if (!fs)
67 return -1;
68
69 for (uint32_t group = 0; group < fs->num_groups; ++group) {
70 uint32_t block_num =
71 alloc_from_bitmap(fs, bitmap_block: fs->group_desc[group].block_bitmap,
72 items_per_group: fs->blocks_per_group, group, update_counts: update_block_counts);
73 if (block_num != (uint32_t) -1) {
74 return block_num;
75 }
76 }
77 return -1;
78}
79
80bool ext2_free_block(struct ext2_fs *fs, uint32_t block_num) {
81 if (!fs || block_num == 0)
82 return false;
83
84 uint32_t group = block_num / fs->blocks_per_group;
85 uint32_t index = block_num % fs->blocks_per_group;
86
87 uint32_t bitmap_block = fs->group_desc[group].block_bitmap;
88
89 struct bcache_entry *ent;
90 uint8_t *bitmap = ext2_block_read(fs, block_num: bitmap_block, out: &ent);
91 if (!bitmap)
92 return false;
93
94 uint32_t byte = index / 8;
95
96 /* already free */
97 if (!BIT_TEST(bitmap[byte], index % 8)) {
98 bcache_ent_release(ent);
99 return false;
100 }
101
102 bitmap[byte] = BIT_CLEAR(bitmap[byte], index % 8);
103 bcache_ent_release(ent);
104 ext2_block_write(fs, ent, EXT2_PRIO_BITMAPS);
105
106 fs->group_desc[group].free_blocks_count++;
107 fs->sblock->free_blocks_count++;
108
109 ext2_write_group_desc(fs);
110 ext2_write_superblock(fs);
111 return true;
112}
113
114inode_t ext2_alloc_inode(struct ext2_fs *fs) {
115 if (!fs)
116 return -1;
117
118 for (uint32_t group = 0; group < fs->num_groups; ++group) {
119 inode_t inode_num =
120 alloc_from_bitmap(fs, bitmap_block: fs->group_desc[group].inode_bitmap,
121 items_per_group: fs->inodes_per_group, group, update_counts: update_inode_counts);
122 if (inode_num != (inode_t) -1) {
123 return inode_num + 1;
124 }
125 }
126 return -1;
127}
128
129bool ext2_free_inode(struct ext2_fs *fs, inode_t inode_num) {
130 if (!fs || inode_num == 0)
131 return false;
132
133 inode_num -= 1;
134 uint32_t group = inode_num / fs->inodes_per_group;
135 uint32_t index = inode_num % fs->inodes_per_group;
136
137 uint32_t bitmap_block = fs->group_desc[group].inode_bitmap;
138
139 struct bcache_entry *ent;
140 uint8_t *bitmap = ext2_block_read(fs, block_num: bitmap_block, out: &ent);
141 if (!bitmap)
142 return false;
143
144 uint32_t byte = index / 8;
145 if (!BIT_TEST(bitmap[byte], index % 8)) {
146 bcache_ent_release(ent);
147 printf(format: "ext2: Inode %u already free\n", inode_num + 1);
148 return false;
149 }
150
151 bitmap[byte] = BIT_CLEAR(bitmap[byte], index % 8);
152 bcache_ent_release(ent);
153 ext2_block_write(fs, ent, EXT2_PRIO_BITMAPS);
154
155 fs->group_desc[group].free_inodes_count++;
156 fs->sblock->free_inodes_count++;
157 ext2_write_group_desc(fs);
158 ext2_write_superblock(fs);
159
160 struct ext2_inode empty = {0};
161 ext2_inode_write(fs, inode_num: inode_num + 1, inode: &empty);
162
163 return true;
164}
165