1#include <errno.h>
2#include <fs/ext2.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <string.h>
6
7bool ext2_dirent_valid(struct ext2_dir_entry *entry) {
8 if (entry->inode == 0 || entry->rec_len < 8 || entry->name_len == 0)
9 return false;
10
11 return true;
12}
13
14static void init_dir(struct ext2_fs *fs, struct ext2_full_inode *dir,
15 uint32_t new_block) {
16 dir->node.block[0] = new_block;
17 dir->node.size = fs->block_size;
18 dir->node.blocks = 2;
19 dir->node.links_count = 2;
20}
21
22static void init_dot_ents(struct ext2_fs *fs, uint8_t *block,
23 struct ext2_full_inode *parent_dir,
24 struct ext2_full_inode *dir) {
25 struct ext2_dir_entry *dot = (struct ext2_dir_entry *) block;
26 dot->inode = dir->inode_num;
27 dot->rec_len = 12;
28 dot->name_len = 1;
29 dot->file_type = EXT2_FT_DIR;
30 strcpy(dest: dot->name, src: ".");
31
32 struct ext2_dir_entry *dotdot = (struct ext2_dir_entry *) (block + 12);
33 dotdot->inode = parent_dir->inode_num;
34 dotdot->rec_len = fs->block_size - 12;
35 dotdot->name_len = 2;
36 dotdot->file_type = EXT2_FT_DIR;
37 strcpy(dest: dotdot->name, src: "..");
38}
39
40enum errno ext2_mkdir(struct ext2_fs *fs, struct ext2_full_inode *parent_dir,
41 const char *name, mode_t mode) {
42 if (!(mode & EXT2_S_IFDIR))
43 mode |= EXT2_S_IFDIR;
44
45 enum errno err = ext2_create_file(fs, parent_dir, name, mode, true);
46 if (err != ERR_OK)
47 return err;
48
49 struct ext2_full_inode *dir;
50 dir = ext2_find_file_in_dir(fs, dir_inode: parent_dir, fname: name, NULL);
51 if (!dir)
52 return ERR_IO;
53
54 uint32_t new_block = ext2_alloc_block(fs);
55 if (new_block == 0) {
56 ext2_dealloc_inode(ino: dir);
57 return ERR_NOSPC;
58 }
59
60 struct bcache_entry *ent;
61 uint8_t *block = ext2_create_bcache_ent(fs, block: new_block, out: &ent);
62
63 if (!block) {
64 ext2_dealloc_inode(ino: dir);
65 return ERR_IO;
66 }
67
68 bcache_ent_acquire(ent);
69 init_dot_ents(fs, block, parent_dir, dir);
70 bcache_ent_release(ent);
71
72 ext2_inode_lock(ino: dir);
73 init_dir(fs, dir, new_block);
74 ext2_inode_unlock(ino: dir);
75
76 ext2_inode_write(fs, inode_num: dir->inode_num, inode: &dir->node);
77 ext2_inode_write(fs, inode_num: parent_dir->inode_num, inode: &parent_dir->node);
78 ext2_block_write(fs, ent, EXT2_PRIO_DIRENT);
79
80 ext2_dealloc_inode(ino: dir);
81 return ERR_OK;
82}
83
84enum errno ext2_rmdir(struct ext2_fs *fs, struct ext2_full_inode *parent_dir,
85 const char *name) {
86 uint8_t type;
87 struct ext2_full_inode *dir;
88 dir = ext2_find_file_in_dir(fs, dir_inode: parent_dir, fname: name, type_out: &type);
89 if (!dir)
90 return ERR_NO_ENT;
91
92 if (!(dir->node.mode & EXT2_S_IFDIR)) {
93 ext2_dealloc_inode(ino: dir);
94 return ERR_NOT_DIR;
95 }
96
97 uint32_t b_idx = 0;
98 uint32_t b_num = 0;
99
100 uint32_t tmp =
101 ext2_get_or_set_block(fs, inode: &dir->node, block_index: b_idx, new_block_num: b_num, false, NULL);
102
103 struct bcache_entry *ent;
104 uint8_t *block = ext2_block_read(fs, block_num: tmp, out: &ent);
105
106 if (!ent) {
107 ext2_dealloc_inode(ino: dir);
108 return ERR_IO;
109 }
110
111 ext2_inode_lock(ino: dir);
112
113 bool empty = true;
114 uint32_t offset = 0;
115
116 while (offset < dir->node.size) {
117 struct ext2_dir_entry *entry;
118 entry = (struct ext2_dir_entry *) (block + offset);
119 if (entry->name_len == 1 && entry->name[0] == '.') {
120 // skip
121 } else if (entry->name_len == 2 && entry->name[0] == '.' &&
122 entry->name[1] == '.') {
123 // skip
124 } else {
125 empty = false;
126 break;
127 }
128 offset += entry->rec_len;
129 }
130
131 bcache_ent_release(ent);
132 ext2_inode_unlock(ino: dir);
133
134 if (!empty) {
135 ext2_dealloc_inode(ino: dir);
136 return ERR_NOT_EMPTY;
137 }
138
139 bool free_blocks = true;
140 bool decrement_links = true;
141
142 enum errno err =
143 ext2_unlink_file(fs, dir_inode: parent_dir, name, free_blocks, decrement_links);
144
145 if (err != ERR_OK) {
146 ext2_dealloc_inode(ino: dir);
147 return err;
148 }
149
150 if (dir->node.blocks) {
151 uint32_t block = dir->node.block[0];
152 if (block)
153 ext2_free_block(fs, block_num: block);
154 dir->node.block[0] = 0;
155 dir->node.blocks = 0;
156 dir->node.size = 0;
157 }
158
159 uint32_t group = ext2_get_inode_group(fs, inode: dir->inode_num);
160 struct ext2_group_desc *desc = &fs->group_desc[group];
161
162 enum irql irql = ext2_fs_lock(fs);
163 desc->used_dirs_count--;
164 parent_dir->node.links_count--;
165 ext2_fs_unlock(fs, i: irql);
166
167 ext2_free_inode(fs, inode_num: dir->inode_num);
168
169 ext2_inode_write(fs, inode_num: dir->inode_num, inode: &dir->node);
170 ext2_inode_write(fs, inode_num: parent_dir->inode_num, inode: &parent_dir->node);
171
172 ext2_dealloc_inode(ino: dir);
173 return ERR_OK;
174}
175