ext2.c

Commit: cf5aa961 Author: jokerz Raw Copy
1
/* ext2.c - Read-only ext2 filesystem driver for Pactor64 */
2
#include "ext2.h"
3
#include "ata.h"
4
5
/* === Partition-aware block read wrapper === */
6
static uint32_t g_part_offset = 0;  /* Partition start LBA */
7
8
static int part_read(uint64_t lba, uint32_t count, void *buf) {
9
    return ata_block_read(lba + g_part_offset, count, buf);
10
}
11
12
/* Mount ext2 from ATA disk at given partition LBA offset */
13
int ext2_mount(ext2_fs_t *fs, uint32_t part_lba) {
14
    if (!ata_is_present()) {
15
        kprintf("[ext2] No ATA drive available\n");
16
        return -1;
17
    }
18
    g_part_offset = part_lba;
19
    kprintf("[ext2] Mounting ext2 from ATA (partition LBA %d)...\n", part_lba);
20
    return ext2_init(fs, part_read);
21
}
22
23
/* Temporary buffers for block reads */
24
static uint8_t block_buf[4096];
25
static uint8_t block_buf2[4096];
26
27
/* Read a single block into buf */
28
static int read_block_raw(ext2_fs_t *fs, uint32_t block, void *buf) {
29
    if (block == 0) {
30
        kmemset(buf, 0, fs->block_size);
31
        return 0;
32
    }
33
    uint32_t sectors_per_block = fs->block_size / 512;
34
    return fs->read_block((uint64_t)block * sectors_per_block, sectors_per_block, buf);
35
}
36
37
/* Read the superblock (always at byte 1024, sector 2) */
38
int ext2_init(ext2_fs_t *fs, block_read_fn read_fn) {
39
    kmemset(fs, 0, sizeof(ext2_fs_t));
40
    fs->read_block = read_fn;
41
42
    /* Read the superblock - it's at byte offset 1024 (sector 2) */
43
    uint8_t sb_buf[1024];
44
    if (read_fn(2, 2, sb_buf) != 0) {
45
        kprintf("[ext2] Failed to read superblock\n");
46
        return -1;
47
    }
48
49
    kmemcpy(&fs->sb, sb_buf, sizeof(ext2_superblock_t));
50
51
    if (fs->sb.s_magic != EXT2_SUPER_MAGIC) {
52
        kprintf("[ext2] Bad magic: 0x%x (expected 0xEF53)\n", fs->sb.s_magic);
53
        return -1;
54
    }
55
56
    /* Calculate block size: 1024 << s_log_block_size */
57
    fs->block_size = 1024U << fs->sb.s_log_block_size;
58
    fs->inodes_per_group = fs->sb.s_inodes_per_group;
59
    fs->inode_size = (fs->sb.s_rev_level >= 1) ? fs->sb.s_inode_size : 128;
60
    fs->groups_count = (fs->sb.s_blocks_count + fs->sb.s_blocks_per_group - 1)
61
                       / fs->sb.s_blocks_per_group;
62
63
    kprintf("[ext2] Magic OK, block_size=%d, inodes=%d, groups=%d\n",
64
            fs->block_size, fs->sb.s_inodes_count, fs->groups_count);
65
    kprintf("[ext2] Volume: %.16s\n", fs->sb.s_volume_name);
66
67
    /* Read block group descriptor table */
68
    static ext2_bgd_t bgdt_buf[64];
69
    fs->bgdt = bgdt_buf;
70
71
    uint32_t bgdt_block = (fs->block_size == 1024) ? 2 : 1;
72
    uint32_t bgdt_bytes = fs->groups_count * sizeof(ext2_bgd_t);
73
    uint32_t bgdt_blocks = (bgdt_bytes + fs->block_size - 1) / fs->block_size;
74
75
    uint8_t *bgdt_ptr = (uint8_t *)fs->bgdt;
76
    for (uint32_t i = 0; i < bgdt_blocks && i * fs->block_size < sizeof(bgdt_buf); i++) {
77
        if (read_block_raw(fs, bgdt_block + i, bgdt_ptr + i * fs->block_size) != 0) {
78
            kprintf("[ext2] Failed to read BGDT block %d\n", bgdt_block + i);
79
            return -1;
80
        }
81
    }
82
83
    kprintf("[ext2] Initialized successfully\n");
84
    return 0;
85
}
86
87
/* Read an inode from the inode table */
88
int ext2_read_inode(ext2_fs_t *fs, uint32_t inode_num, ext2_inode_t *inode) {
89
    if (inode_num == 0) return -1;
90
91
    uint32_t group = (inode_num - 1) / fs->inodes_per_group;
92
    uint32_t index = (inode_num - 1) % fs->inodes_per_group;
93
    if (group >= fs->groups_count) return -1;
94
95
    uint32_t offset = index * fs->inode_size;
96
    uint32_t block_offset = offset / fs->block_size;
97
    uint32_t byte_offset = offset % fs->block_size;
98
    uint32_t table_block = fs->bgdt[group].bg_inode_table + block_offset;
99
100
    if (read_block_raw(fs, table_block, block_buf) != 0) {
101
        kprintf("[ext2] Failed to read inode table block %d\n", table_block);
102
        return -1;
103
    }
104
105
    kmemcpy(inode, block_buf + byte_offset, sizeof(ext2_inode_t));
106
    return 0;
107
}
108
109
/* Get physical block number from file block index */
110
static uint32_t get_block_num(ext2_fs_t *fs, ext2_inode_t *inode, uint32_t file_block) {
111
    uint32_t ptrs_per_block = fs->block_size / 4;
112
113
    /* Direct blocks (0-11) */
114
    if (file_block < 12) return inode->i_block[file_block];
115
    file_block -= 12;
116
117
    /* Single indirect */
118
    if (file_block < ptrs_per_block) {
119
        uint32_t indirect = inode->i_block[12];
120
        if (indirect == 0) return 0;
121
        read_block_raw(fs, indirect, block_buf);
122
        return ((uint32_t *)block_buf)[file_block];
123
    }
124
    file_block -= ptrs_per_block;
125
126
    /* Double indirect */
127
    if (file_block < ptrs_per_block * ptrs_per_block) {
128
        uint32_t dindirect = inode->i_block[13];
129
        if (dindirect == 0) return 0;
130
        read_block_raw(fs, dindirect, block_buf);
131
        uint32_t *ptrs1 = (uint32_t *)block_buf;
132
        uint32_t idx1 = file_block / ptrs_per_block;
133
        uint32_t idx2 = file_block % ptrs_per_block;
134
        if (ptrs1[idx1] == 0) return 0;
135
        read_block_raw(fs, ptrs1[idx1], block_buf2);
136
        return ((uint32_t *)block_buf2)[idx2];
137
    }
138
139
    return 0; /* Triple indirect not implemented */
140
}
141
142
/* Read bytes from an inode at a given offset */
143
static int read_inode_data(ext2_fs_t *fs, ext2_inode_t *inode,
144
                           uint32_t offset, uint32_t size, void *buf) {
145
    uint32_t file_size = inode->i_size;
146
    uint8_t *dst = (uint8_t *)buf;
147
    uint32_t bs = fs->block_size;
148
149
    while (size > 0 && offset < file_size) {
150
        uint32_t file_block = offset / bs;
151
        uint32_t block_off = offset % bs;
152
        uint32_t to_read = bs - block_off;
153
        if (to_read > size) to_read = size;
154
        if (offset + to_read > file_size) to_read = file_size - offset;
155
156
        uint32_t phys_block = get_block_num(fs, inode, file_block);
157
        if (phys_block == 0) {
158
            kmemset(dst, 0, to_read);
159
        } else {
160
            if (read_block_raw(fs, phys_block, block_buf) != 0) return -1;
161
            kmemcpy(dst, block_buf + block_off, to_read);
162
        }
163
        dst += to_read;
164
        offset += to_read;
165
        size -= to_read;
166
    }
167
    return (int)(dst - (uint8_t *)buf);
168
}
169
170
/* Find a directory entry by name */
171
static int find_dirent(ext2_fs_t *fs, ext2_inode_t *dir_inode,
172
                       const char *name, uint32_t name_len,
173
                       uint32_t *out_inode, uint8_t *out_type) {
174
    uint32_t offset = 0;
175
    uint32_t dir_size = dir_inode->i_size;
176
177
    while (offset < dir_size) {
178
        ext2_dirent_t ent;
179
        if (read_inode_data(fs, dir_inode, offset, sizeof(ext2_dirent_t), &ent) < (int)sizeof(ext2_dirent_t))
180
            return -1;
181
        if (ent.rec_len == 0) return -1;
182
183
        if (ent.inode != 0 && ent.name_len == name_len) {
184
            char ent_name[EXT2_NAME_LEN + 1];
185
            if (read_inode_data(fs, dir_inode, offset + sizeof(ext2_dirent_t),
186
                                ent.name_len, ent_name) < (int)ent.name_len)
187
                return -1;
188
            ent_name[ent.name_len] = '\0';
189
            if (kstrncmp(ent_name, name, name_len) == 0) {
190
                *out_inode = ent.inode;
191
                *out_type = ent.file_type;
192
                return 0;
193
            }
194
        }
195
        offset += ent.rec_len;
196
    }
197
    return -1;
198
}
199
200
/* Parse a path and open the file */
201
int ext2_open(ext2_fs_t *fs, const char *path, ext2_file_t *file) {
202
    kmemset(file, 0, sizeof(ext2_file_t));
203
    file->fs = fs;
204
205
    uint32_t cur_ino = EXT2_ROOT_INO;
206
    ext2_inode_t cur_inode;
207
208
    if (ext2_read_inode(fs, cur_ino, &cur_inode) != 0) {
209
        kprintf("[ext2] Failed to read root inode\n");
210
        return -1;
211
    }
212
213
    const char *p = path;
214
    if (*p == '/') p++;
215
216
    while (*p) {
217
        const char *comp_start = p;
218
        uint32_t comp_len = 0;
219
        while (*p && *p != '/') { p++; comp_len++; }
220
        if (comp_len == 0) continue;
221
222
        if (!(cur_inode.i_mode & EXT2_S_IFDIR)) return -1;
223
224
        uint32_t child_ino;
225
        uint8_t child_type;
226
        if (find_dirent(fs, &cur_inode, comp_start, comp_len, &child_ino, &child_type) != 0) {
227
            kprintf("[ext2] Not found: %.*s\n", comp_len, comp_start);
228
            return -1;
229
        }
230
231
        cur_ino = child_ino;
232
        if (ext2_read_inode(fs, cur_ino, &cur_inode) != 0) return -1;
233
        if (*p == '/') p++;
234
    }
235
236
    file->inode = cur_inode;
237
    file->inode_num = cur_ino;
238
    file->size = cur_inode.i_size;
239
    file->position = 0;
240
241
    kprintf("[ext2] Opened inode %d, size=%d bytes\n", cur_ino, file->size);
242
    return 0;
243
}
244
245
/* Read from an open file */
246
int ext2_read(ext2_file_t *file, void *buf, uint32_t size) {
247
    if (file->position >= file->size) return 0;
248
    if (file->position + size > file->size) size = file->size - file->position;
249
250
    int bytes = read_inode_data(file->fs, &file->inode, file->position, size, buf);
251
    if (bytes > 0) file->position += bytes;
252
    return bytes;
253
}
254
255
void ext2_close(ext2_file_t *file) {
256
    file->position = 0;
257
    file->size = 0;
258
}
259
260
/* List directory entries via callback */
261
int ext2_readdir(ext2_fs_t *fs, uint32_t dir_inode, ext2_dirent_cb cb, void *ctx) {
262
    ext2_inode_t inode;
263
    if (ext2_read_inode(fs, dir_inode, &inode) != 0) return -1;
264
    if (!(inode.i_mode & EXT2_S_IFDIR)) return -1;
265
266
    uint32_t offset = 0;
267
    uint32_t dir_size = inode.i_size;
268
269
    while (offset < dir_size) {
270
        ext2_dirent_t ent;
271
        if (read_inode_data(fs, &inode, offset, sizeof(ext2_dirent_t), &ent) < (int)sizeof(ext2_dirent_t))
272
            return -1;
273
        if (ent.rec_len == 0) return -1;
274
275
        if (ent.inode != 0) {
276
            char name[EXT2_NAME_LEN + 1];
277
            if (read_inode_data(fs, &inode, offset + sizeof(ext2_dirent_t),
278
                                ent.name_len, name) < (int)ent.name_len)
279
                return -1;
280
            name[ent.name_len] = '\0';
281
            cb(name, ent.name_len, ent.inode, ent.file_type, ctx);
282
        }
283
        offset += ent.rec_len;
284
    }
285
    return 0;
286
}
287