1#include <fs/ext2.h>
2#include <stdbool.h>
3#include <stdint.h>
4#include <string.h>
5
6static uint32_t ext2_get_block(struct ext2_fs *fs, uint32_t block_num,
7 uint32_t depth, uint32_t block_index,
8 uint32_t new_block_num, bool allocate,
9 bool *was_allocated) {
10 if (depth == 0) {
11 // actual data block
12 return block_num;
13 }
14
15 uint32_t pointers_per_block = fs->block_size / sizeof(uint32_t);
16
17 bool allocated_this_level = false;
18 struct bcache_entry *ent;
19 uint32_t *block = (uint32_t *) ext2_block_read(fs, block_num, out: &ent);
20
21 if (block_num == 0) {
22 if (!allocate) {
23 return 0;
24 }
25
26 block_num = ext2_alloc_block(fs);
27 if (block_num == 0) {
28 return 0;
29 }
30
31 allocated_this_level = true;
32
33 block = (uint32_t *) ext2_block_read(fs, block_num, out: &ent);
34 }
35
36 if (allocated_this_level) {
37 memset(block, 0, fs->block_size);
38 }
39
40 uint32_t index = block_index;
41 uint32_t divisor = 1;
42 for (uint32_t i = 1; i < depth; i++)
43 divisor *= pointers_per_block;
44
45 uint32_t entry_index = index / divisor;
46 uint32_t entry_offset = index % divisor;
47 uint32_t bnum = block[entry_index];
48 bcache_ent_release(ent);
49
50 uint32_t result;
51 result = ext2_get_block(fs, block_num: bnum, depth: depth - 1, block_index: entry_offset, new_block_num,
52 allocate, was_allocated);
53 bcache_ent_acquire(ent);
54
55 if (result && block[entry_index] == 0 && allocate) {
56 block[entry_index] = result;
57 bcache_ent_release(ent);
58
59 ext2_block_write(fs, ent, EXT2_PRIO_DIRENT);
60
61 bcache_ent_acquire(ent);
62 } else if (allocated_this_level && result == 0) {
63 ext2_free_block(fs, block_num);
64 }
65
66 bcache_ent_release(ent);
67
68 return result;
69}
70
71uint32_t ext2_get_or_set_block(struct ext2_fs *fs, struct ext2_inode *inode,
72 uint32_t block_index, uint32_t new_block_num,
73 bool allocate, bool *was_allocated) {
74 if (!fs || !inode)
75 return (uint32_t) -1;
76
77 if (was_allocated)
78 *was_allocated = false;
79
80 uint32_t pointers_per_block = fs->block_size / sizeof(uint32_t);
81
82 if (block_index < 12) {
83 if (inode->block[block_index] == 0 && allocate) {
84 if (new_block_num == 0)
85 new_block_num = ext2_alloc_block(fs);
86
87 if (new_block_num == 0)
88 return 0;
89
90 inode->block[block_index] = new_block_num;
91 if (was_allocated)
92 *was_allocated = true;
93 }
94 return inode->block[block_index];
95 }
96
97 block_index -= 12;
98
99 if (block_index < pointers_per_block) {
100 return ext2_get_block(fs, block_num: inode->block[12], depth: 1, block_index,
101 new_block_num, allocate, was_allocated);
102 }
103
104 block_index -= pointers_per_block;
105
106 if (block_index < pointers_per_block * pointers_per_block) {
107 return ext2_get_block(fs, block_num: inode->block[13], depth: 2, block_index,
108 new_block_num, allocate, was_allocated);
109 }
110
111 block_index -= pointers_per_block * pointers_per_block;
112
113 uint64_t max_triple =
114 (uint64_t) pointers_per_block * pointers_per_block * pointers_per_block;
115 if ((uint64_t) block_index < max_triple) {
116 return ext2_get_block(fs, block_num: inode->block[14], depth: 3, block_index,
117 new_block_num, allocate, was_allocated);
118 }
119
120 return (uint32_t) -1;
121}
122
123void ext2_init_inode(struct ext2_inode *new_inode, mode_t mode) {
124 new_inode->mode = mode;
125 new_inode->uid = 0;
126 new_inode->gid = 0; // TODO: these
127 new_inode->size = 0;
128 new_inode->atime = time_get_unix();
129 new_inode->ctime = new_inode->mtime = new_inode->atime;
130 new_inode->links_count = ((mode & EXT2_S_IFDIR) ? 2 : 1);
131 new_inode->blocks = 0;
132 new_inode->flags = 0;
133}
134
135void ext2_init_dirent(struct ext2_fs *fs, struct ext2_dir_entry *new_entry,
136 inode_t inode_num, const char *name, uint8_t type) {
137 new_entry->inode = inode_num;
138 new_entry->name_len = strlen(str: name);
139 new_entry->rec_len = fs->block_size;
140 new_entry->file_type = type;
141 memcpy(new_entry->name, name, new_entry->name_len);
142}
143
144uint8_t ext2_extract_ftype(mode_t mode) {
145 uint8_t file_type;
146 if (mode & EXT2_S_IFDIR)
147 file_type = EXT2_FT_DIR;
148 else if (mode & EXT2_S_IFREG)
149 file_type = EXT2_FT_REG_FILE;
150 else if (mode & EXT2_S_IFLNK)
151 file_type = EXT2_FT_SYMLINK;
152 else
153 file_type = EXT2_FT_UNKNOWN;
154
155 return file_type;
156}
157