blob: 1ac52b9149dda40bcc4ee353c0c75ac357f6b4c9 [file] [log] [blame]
swissChili4749d022021-07-19 12:33:06 -07001#include <alloc.h>
swissChili4418ca52021-06-14 17:36:00 -07002#include <dri/ata_pio/ata_pio.h>
swissChilie5adca52021-06-16 21:00:31 -07003#include <dri/fs/ext2/ext2.h>
swissChili4749d022021-07-19 12:33:06 -07004#include <io.h>
swissChilief829f32021-06-13 20:00:54 -07005#include <kint.h>
swissChili4418ca52021-06-14 17:36:00 -07006#include <log.h>
swissChilib7ef65d2021-07-17 12:51:52 -07007
8inline uint ext2_block_size(struct ext2_superblock *sb)
9{
10 return 1024 << sb->block_size_shift;
11}
12
13void ext2_read_block(struct ext2_superblock *sb, void *buffer, uint block)
14{
15 uint block_size = ext2_block_size(sb) / 512;
16 uint block_start = block_size * block;
17
18 ata_pio_read_sectors(buffer, block_start, block_size);
19}
swissChili4418ca52021-06-14 17:36:00 -070020
swissChili4749d022021-07-19 12:33:06 -070021void ext2_write_block(struct ext2_superblock *sb, void *buffer, uint block)
22{
23 uint block_size = ext2_block_size(sb) / 512;
24 uint block_start = block_size * block;
25
26 ata_pio_write_sectors(block_start, block_size, buffer);
27}
28
swissChili4418ca52021-06-14 17:36:00 -070029struct ext2_superblock ext2_read_superblock()
30{
31 uchar buffer[512 * 2];
32 ata_pio_read_sectors(buffer, 2, 2);
33
34 struct ext2_superblock *sb = (void *)(buffer);
35 return *sb;
36}
swissChilief829f32021-06-13 20:00:54 -070037
swissChilie5adca52021-06-16 21:00:31 -070038uint ext2_num_block_groups(struct ext2_superblock *sb)
39{
40 // This is a mildly janky way of rounding up
swissChili36ed5d72021-07-23 14:56:36 -070041 uint a = IDIV_CEIL(sb->total_blocks, sb->blocks_per_block_group);
42 uint b = IDIV_CEIL(sb->total_inodes, sb->inodes_per_block_group);
swissChilie5adca52021-06-16 21:00:31 -070043
44 if (a == b)
45 {
46 return a;
47 }
48 else
49 {
50 kprintf(ERROR "EXT2 cannot find number of block groups, %d and %d "
51 "should equal.\n",
52 a, b);
53 kpanic("Corrupted filesystem");
54 }
55}
56
swissChilib7ef65d2021-07-17 12:51:52 -070057struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
58 struct ext2_superblock *sb, uint block_group)
swissChilie5adca52021-06-16 21:00:31 -070059{
60 /**
61 * The BGDT (not to be confused with the GDT) is located the block after the
62 * superblock. On any block size EXCEPT 1024 (the minimum, remember that the
63 * block size is specified by X where 1024 << X is the real size) this is
64 * the second block (0-indexed, so 1). On 1024 this is the third block.
65 */
66 uint bgdt_block = 1;
67 uint block_size = ext2_block_size(sb);
68
69 if (block_size == 1024)
70 bgdt_block = 2;
71
swissChilib7ef65d2021-07-17 12:51:52 -070072 const uint bgd_size = sizeof(struct ext2_block_group_descriptor);
73
74 // Disk page that the BGD is on relative to the initial FILE SYSTEM block
75 uint hd_page = block_group / (512 / bgd_size);
76 // The offset from the beginning of that page the BGD is at
77 uint bgd_offset = block_group % (512 / bgd_size);
78
79 struct ext2_block_group_descriptor descriptors[512 / bgd_size];
80 kassert(sizeof(descriptors) == 512, "Wrong BGD size");
81
82 uint lba = (block_size / 512) * bgdt_block + hd_page;
83
84 ata_pio_read_sectors(&descriptors, lba, 1);
85
86 return descriptors[bgd_offset];
87}
88
swissChilicbd43632021-07-17 16:19:44 -070089static void print_entry(uint inode, const char *name, void *sb)
swissChilib7ef65d2021-07-17 12:51:52 -070090{
91 kprintf("%d\t %s\n", inode, name);
swissChilicbd43632021-07-17 16:19:44 -070092
swissChili36ed5d72021-07-23 14:56:36 -070093 return;
94
swissChilicbd43632021-07-17 16:19:44 -070095 struct ext2_inode in;
96
97 if (ext2_find_inode(sb, inode, &in))
98 {
99 if ((in.mode & EXT2_F_TYPE) == EXT2_S_IFREG)
100 {
101 char buffer[65];
102 uint read = ext2_read_inode(sb, &in, buffer, 64);
103 buffer[read] = 0;
104
105 kprintf("contents: %d\n'%s'\n", read, buffer);
106 }
107 }
108
109 return;
swissChilie5adca52021-06-16 21:00:31 -0700110}
111
swissChilief829f32021-06-13 20:00:54 -0700112void ext2_mount(struct fs_node *where)
113{
swissChili4418ca52021-06-14 17:36:00 -0700114 struct ext2_superblock sb = ext2_read_superblock();
115
swissChilie5adca52021-06-16 21:00:31 -0700116 kprintf(DEBUG "EXT2 magic = 0x%x\n", sb.signature);
swissChili4749d022021-07-19 12:33:06 -0700117
swissChilib7ef65d2021-07-17 12:51:52 -0700118 // Read the root inode 2
119 struct ext2_inode root;
swissChilie5adca52021-06-16 21:00:31 -0700120
swissChilib7ef65d2021-07-17 12:51:52 -0700121 if (ext2_find_inode(&sb, 2, &root))
122 {
swissChili4749d022021-07-19 12:33:06 -0700123 kprintf(OKAY "Found root inode 2 (size=0x%x, num_blocks=0x%x)\n",
124 root.size, root.num_blocks);
swissChilib7ef65d2021-07-17 12:51:52 -0700125 // kprintf(DEBUG "Root.mode = 0x%x\n", root.mode & 0xf000);
swissChili4749d022021-07-19 12:33:06 -0700126 kassert((root.mode & 0xf000) == EXT2_S_IFDIR,
127 "Root (inode 2) is not a directory.");
swissChilib7ef65d2021-07-17 12:51:52 -0700128
swissChili36ed5d72021-07-23 14:56:36 -0700129 char *name = "hello-hl.txt";
130 kprintf(INFO "Creating hard link %s -> hello.txt\n", name);
131 ext2_insert_into_dir(&sb, &root, name, strlen(name), 12,
132 EXT2_FT_REGULAR_FILE);
133
swissChilib7ef65d2021-07-17 12:51:52 -0700134 kprintf("ls /\n");
135 kprintf("inode\t name\n");
136 kprintf("--------------------\n");
swissChilicbd43632021-07-17 16:19:44 -0700137 ext2_dir_ls(&sb, &root, print_entry, &sb);
swissChilib7ef65d2021-07-17 12:51:52 -0700138 }
139 else
140 {
141 kprintf(WARN "Failed to find root inode 2\n");
142 }
swissChili4749d022021-07-19 12:33:06 -0700143
144 kprintf(INFO "First free inode is %d\n", ext2_first_free_inode(&sb));
swissChilief829f32021-06-13 20:00:54 -0700145}
swissChili9bd74de2021-06-15 20:30:48 -0700146
147bool ext2_valid_filesystem()
148{
149 struct ext2_superblock sb = ext2_read_superblock();
150
swissChili4749d022021-07-19 12:33:06 -0700151 kprintf(DEBUG "superblock signature is %d (0x%x)\n", sb.signature,
152 sb.signature);
swissChili276b8cf2021-07-16 13:24:42 -0700153
swissChili9bd74de2021-06-15 20:30:48 -0700154 return sb.signature == EXT2_SIGNATURE;
swissChilie5adca52021-06-16 21:00:31 -0700155}
swissChilib7ef65d2021-07-17 12:51:52 -0700156
157void ext2_write_superblock(struct ext2_superblock *sb)
158{
159 ushort *wp = (ushort *)sb;
160
161 ata_pio_write_sectors(2, 2, wp);
162}
163
164void ext2_corrupt_superblock_for_fun()
165{
166 struct ext2_superblock sb = ext2_read_superblock();
167 sb.signature = 0xDEAD;
168 ext2_write_superblock(&sb);
169}
170
swissChili4749d022021-07-19 12:33:06 -0700171bool ext2_find_inode(struct ext2_superblock *sb, uint number,
172 struct ext2_inode *inode)
swissChilib7ef65d2021-07-17 12:51:52 -0700173{
174 if (number == 0)
175 return false;
176
177 uint block_group = (number - 1) / sb->inodes_per_block_group;
178 uint local_index = (number - 1) % sb->inodes_per_block_group;
179
180 // Load this from the block group descriptor table
181 struct ext2_block_group_descriptor descriptor =
182 ext2_load_block_group_descriptor(sb, block_group);
183
swissChili4749d022021-07-19 12:33:06 -0700184 // kprintf(DEBUG "Descriptor inode_table = 0x%x\n",
185 // descriptor.inode_table_start_block);
swissChilib7ef65d2021-07-17 12:51:52 -0700186
187 // We need to figure out what FS block the inode is on, we know how many
188 // inodes there are total in this BGD and the number per page, so this is
189 // simple.
190
191 const uint block_size = ext2_block_size(sb);
192
193 const uint inodes_per_block = block_size / sizeof(struct ext2_inode);
194
195 uint inode_block = local_index / inodes_per_block;
196 uint inode_index = local_index % inodes_per_block;
197
198 struct ext2_inode inodes[block_size / sizeof(struct ext2_inode)];
199
swissChili4749d022021-07-19 12:33:06 -0700200 ext2_read_block(sb, inodes,
201 descriptor.inode_table_start_block + inode_block);
swissChilib7ef65d2021-07-17 12:51:52 -0700202
203 *inode = inodes[inode_index];
204
205 return true;
206}
207
swissChili4749d022021-07-19 12:33:06 -0700208bool ext2_dir_ls(struct ext2_superblock *sb, struct ext2_inode *dir,
209 void (*cb)(uint inode, const char *name, void *data),
swissChilib7ef65d2021-07-17 12:51:52 -0700210 void *data)
211{
212 if ((dir->mode & 0xf000) != EXT2_S_IFDIR)
213 return false;
214
215 for (int i = 0; i < dir->num_blocks; i++)
216 {
217 uchar buffer[ext2_block_size(sb)];
218 ext2_read_inode_block(sb, dir, buffer, i);
219
220 struct ext2_dirent *ent = (void *)buffer;
221
222 // While there are files in this block
swissChilicbd43632021-07-17 16:19:44 -0700223 while ((uint)ent < (uint)(buffer + ext2_block_size(sb)))
swissChilib7ef65d2021-07-17 12:51:52 -0700224 {
225 if (ent->inode == 0)
226 return true;
227
228 if (cb)
229 {
swissChili36ed5d72021-07-23 14:56:36 -0700230 char name[256];
231 uint name_len = MIN(ent->name_len, 255);
swissChilib7ef65d2021-07-17 12:51:52 -0700232
swissChili36ed5d72021-07-23 14:56:36 -0700233 memcpy(name, ent->name, name_len);
234 name[name_len] = '\0';
swissChilicbd43632021-07-17 16:19:44 -0700235
swissChili36ed5d72021-07-23 14:56:36 -0700236 // kprintf("@ b=%d,ent=%p\n", i, (uint)ent - (uint)buffer);
swissChilib7ef65d2021-07-17 12:51:52 -0700237 cb(ent->inode, name, data);
238 }
239
240 ent = (void *)(((uint)(void *)ent) + ent->rec_len);
241 }
242 // We ran out of files in this block, continue to the next one. This
243 // works because files cannot span blocks
244 }
245
246 return true;
247}
248
swissChili36ed5d72021-07-23 14:56:36 -0700249static void ext2_show_dirent(struct ext2_dirent *ent)
250{
251 char name[ent->name_len + 1];
252 memcpy(name, ent->name, ent->name_len);
253 name[ent->name_len] = '\0';
254
255 kprintf(DEBUG "<ent ft=%p, i=%d, s=%s, l=%d>\n", ent->file_type, ent->inode,
256 ent->name, ent->rec_len);
257}
258
259void ext2_insert_into_dir(struct ext2_superblock *sb, struct ext2_inode *dir,
260 char *name, uint name_len, uint inode, uchar type)
261{
262 name_len = MIN(name_len, 255);
263 const uint min_size = PAD(name_len + sizeof(struct ext2_dirent));
264 const uint block_size = ext2_block_size(sb);
265
266 if ((dir->mode & 0xf000) != EXT2_S_IFDIR)
267 return;
268
269 uchar buffer[block_size];
270 uint i; // block #
271
272 for (i = 0; i < dir->num_blocks; i++)
273 {
274 ext2_read_inode_block(sb, dir, buffer, i);
275
276 struct ext2_dirent *ent = (void *)buffer;
277
278 // While there are files in this block
279 while ((uint)ent < (uint)(buffer + block_size))
280 {
281 // kprintf(" %d@%db,%d-%d", ent->inode, i, (uint)ent - (uint)buffer,
282 // ent->rec_len);
283 if (ent->inode == 0)
284 {
285 // This is the last item, just insert it at the end
286 // TODO: check this actually fits!
287 // TODO: if we are in a new block, actually create it!
288
289 ent->rec_len = min_size;
290 ent->name_len = name_len;
291 memcpy(ent->name, name, name_len);
292 ent->inode = inode;
293 ent->file_type = type;
294
295 kprintf(DEBUG
296 "Inserted into dir (appending) at block=%d, b=%p \n",
297 i, (uint)ent - (uint)buffer);
298
299 goto finish;
300 }
301
302 uint this_min_size =
303 PAD(ent->name_len + sizeof(struct ext2_dirent));
304 uint available_size = ent->rec_len - this_min_size;
305
306 // kprintf(",%d=%d/%d", ent->name_len, this_min_size, available_size);
307
308 if (available_size >= min_size)
309 {
310 // We can fit this in here
311 struct ext2_dirent *inserting =
312 (void *)(((uint)(void *)ent) + this_min_size);
313
314 ent->rec_len = this_min_size;
315
316 inserting->rec_len = available_size;
317 inserting->name_len = name_len;
318 inserting->inode = inode;
319 inserting->file_type = type;
320 memcpy(inserting->name, name, name_len);
321
322 kprintf(DEBUG
323 "Inserted into dir (splicing) at block=%d, b=%p \n",
324 i, (uint)inserting - (uint)buffer);
325
326 // Done!
327 goto finish;
328 }
329
330 ent = (void *)(((uint)(void *)ent) + ent->rec_len);
331 }
332 // We ran out of files in this block, continue to the next one. This
333 // works because files cannot span blocks
334 }
335
336 kprintf("\n");
337
338 kprintf(WARN "Failed to insert!\n");
339
340finish:
341 kprintf("\n");
342 ext2_write_inode_block(sb, dir, buffer, i);
343 kprintf(DEBUG "[insert] writing inode block %d\n", i);
344}
345
swissChili4749d022021-07-19 12:33:06 -0700346ssize_t ext2_read_inode(struct ext2_superblock *sb, struct ext2_inode *inode,
347 void *buffer, ssize_t size)
swissChilicbd43632021-07-17 16:19:44 -0700348{
349 const uint block_size = ext2_block_size(sb);
350 char transfer[block_size];
351
352 uint fsize = MIN(inode->size, size);
353 uint i;
354
355 // Transfer full blocks straight to the output buffer
356 for (i = 0; i < fsize / block_size; i++)
357 {
358 ext2_read_inode_block(sb, inode, buffer + i * block_size, i);
359 }
360
swissChili4749d022021-07-19 12:33:06 -0700361 // If we have part of a block left over read it here first, then transfer
362 // what we need
swissChilicbd43632021-07-17 16:19:44 -0700363 if (i * block_size < fsize)
364 {
365 uint remainder = fsize % block_size;
366
367 ext2_read_inode_block(sb, inode, transfer, i);
368 memcpy(buffer + i * block_size, transfer, remainder);
369 }
370
371 return fsize;
372}
373
swissChili36ed5d72021-07-23 14:56:36 -0700374static uint ext2_compute_absolute_block(struct ext2_superblock *sb,
375 struct ext2_inode *inode, uint block)
swissChilib7ef65d2021-07-17 12:51:52 -0700376{
377 if (block >= 12)
378 {
swissChili4749d022021-07-19 12:33:06 -0700379 kprintf(ERROR
380 "Sorry, EXT2 can only access the first 12 (direct) blocks "
381 "of an inode for now. Indirect look-up will be added later\n");
swissChilib7ef65d2021-07-17 12:51:52 -0700382 kpanic("Invalid inode block");
383 }
384
swissChili36ed5d72021-07-23 14:56:36 -0700385 return block;
386}
387
388bool ext2_read_inode_block(struct ext2_superblock *sb, struct ext2_inode *inode,
389 void *buffer, uint block)
390{
391 block = ext2_compute_absolute_block(sb, inode, block);
swissChilib7ef65d2021-07-17 12:51:52 -0700392 uint block_address = inode->blocks[block];
393
394 ext2_read_block(sb, buffer, block_address);
swissChilicbd43632021-07-17 16:19:44 -0700395
396 return true;
swissChilib7ef65d2021-07-17 12:51:52 -0700397}
swissChili4749d022021-07-19 12:33:06 -0700398
swissChili36ed5d72021-07-23 14:56:36 -0700399bool ext2_write_inode_block(struct ext2_superblock *sb, struct ext2_inode *dir,
400 void *buffer, uint block)
401{
402 block = ext2_compute_absolute_block(sb, dir, block);
403 uint block_address = dir->blocks[block];
404
405 kprintf(DEBUG "Writing size=%d, inode block %p, b=%d\n",
406 ext2_block_size(sb), block_address * ext2_block_size(sb), block);
407
408 ext2_write_block(sb, buffer, block_address);
409
410 return true;
411}
412
swissChili4749d022021-07-19 12:33:06 -0700413static const uint ext2_bitmap_block(struct ext2_superblock *sb,
414 uint *bitmap_block, uint *index)
415{
416 const uint block_size = ext2_block_size(sb);
417
swissChilicaa24782021-07-19 14:29:58 -0700418 while (*index > block_size)
swissChili4749d022021-07-19 12:33:06 -0700419 {
swissChilicaa24782021-07-19 14:29:58 -0700420 *index -= block_size;
421 *bitmap_block += 1;
swissChili4749d022021-07-19 12:33:06 -0700422 }
423
424 return block_size;
425}
426
427bool ext2_check_in_bitmap(struct ext2_superblock *sb, uint bitmap_block,
428 uint index)
429{
430 const uint block_size = ext2_bitmap_block(sb, &bitmap_block, &index);
431
432 uint byte = index / 8;
433 uint bit = index % 8;
434
435 uchar buffer[block_size];
436
437 ext2_read_block(sb, buffer, bitmap_block);
438
439 return !!(buffer[byte] & (1 << bit));
440}
441
442void ext2_set_in_bitmap(struct ext2_superblock *sb, uint bitmap_block,
443 uint index, bool value)
444{
445 const uint block_size = ext2_bitmap_block(sb, &bitmap_block, &index);
446
447 uint byte = index / 8;
448 uint bit = index % 8;
449
450 uchar buffer[block_size];
451
452 ext2_read_block(sb, buffer, bitmap_block);
453
454 uchar target_bit = 1 << bit;
455 buffer[byte] =
456 value ? (buffer[byte] | target_bit) : (buffer[byte] | ~target_bit);
457
458 ext2_write_block(sb, buffer, bitmap_block);
459}
460
461uint ext2_first_zero_bit(struct ext2_superblock *sb, uint bitmap_block,
462 uint num_blocks, uint start_at)
463{
464 uint block_size = ext2_block_size(sb);
465
466 for (uint block = bitmap_block; block < bitmap_block + num_blocks; block++)
467 {
468 // dword-array for performance
469 uint buffer[block_size / 4];
470
471 ext2_read_block(sb, buffer, block);
472
473 // If this is the first block start at start_at, otherwise 0
474 for (int i = 0; i < block_size / 4; i++)
475 {
476 // The bitwise negative will be non-zero if there are zero bits in
477 // the original.
478 if (~buffer[i])
479 {
480 // 4 bytes * 8 bits * i dwords
481 uint index =
482 (4 * 8 * i) + (block - bitmap_block) * 8 * block_size;
483
swissChilicaa24782021-07-19 14:29:58 -0700484 // __builtin_ffs gives us 1+the index of the least-significant 1
swissChili4749d022021-07-19 12:33:06 -0700485 // bit. Since we take the bitwise inverse this is actuall the
486 // least significant 0 bit. This is a GCC intrinsic. This works
487 // particularly well on little-endian systems where the least
488 // significant bit happens to also correspond to the first bit
489 // in the dword bitset.
490 //
491 // ________ ________ ________ ________
492 // ^ this is the LSB ^
493 // | this is the MSB
494 //
495 // This means that the LSB is also the first bit in the bitset.
swissChilicaa24782021-07-19 14:29:58 -0700496 uint trailing = __builtin_ffs(~buffer[i]) - 1;
swissChili4749d022021-07-19 12:33:06 -0700497
swissChili4749d022021-07-19 12:33:06 -0700498 return trailing + index;
499 }
500 }
501 }
502
503 return -1;
504}
505
506uint ext2_first_free_inode(struct ext2_superblock *sb)
507{
swissChili36ed5d72021-07-23 14:56:36 -0700508 uint num_block_groups = ext2_num_block_groups(sb);
swissChili4749d022021-07-19 12:33:06 -0700509
swissChili36ed5d72021-07-23 14:56:36 -0700510 kprintf(INFO "%d block groups\n", num_block_groups);
swissChili4749d022021-07-19 12:33:06 -0700511
swissChili36ed5d72021-07-23 14:56:36 -0700512 for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
swissChili4749d022021-07-19 12:33:06 -0700513 {
swissChili36ed5d72021-07-23 14:56:36 -0700514 struct ext2_block_group_descriptor bgd =
515 ext2_load_block_group_descriptor(sb, 0);
516
517 const uint block_size = ext2_block_size(sb);
518 // + 1 because we need to round up (ie 1025 for 1024 size blocks will
519 // yield 1, should 2)
520 uint bitset_blocks = (sb->inodes_per_block_group / 8) / block_size + 1;
521
522 // inodes start at 1
523 uint inode =
524 ext2_first_zero_bit(sb, bgd.inode_bitmap, bitset_blocks, 12) + 1;
525 // This will overflow back to zero if no inode was found
526
527 if (inode)
528 return inode;
swissChili4749d022021-07-19 12:33:06 -0700529 }
530
swissChili36ed5d72021-07-23 14:56:36 -0700531 return 0;
532}
533
534// ^
535// | this and | this are awfully similar, should refactor
536// v
537
538uint ext2_first_free_block(struct ext2_superblock *sb)
539{
540 uint num_block_groups = ext2_num_block_groups(sb);
541
542 for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
543 {
544 struct ext2_block_group_descriptor bgd =
545 ext2_load_block_group_descriptor(sb, 0);
546
547 const uint block_size = ext2_block_size(sb);
548 // + 1 because we need to round up (ie 1025 for 1024 size blocks will
549 // yield 1, should 2)
550 uint bitset_blocks = (sb->blocks_per_block_group / 8) / block_size + 1;
551
552 // inodes start at 1
553 uint block_no =
554 ext2_first_zero_bit(sb, bgd.block_bitmap, bitset_blocks, 0);
555 // This will overflow back to zero if no inode was found
556
557 if (block_no != 0xffffffff)
558 return block_no;
559 }
560
561 return 0;
swissChili4749d022021-07-19 12:33:06 -0700562}