1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#pragma once
#include <FSKit/Ext2.h>
#include <KernelKit/DriveMgr.h>
#include <KernelKit/HeapMgr.h>
#include <KernelKit/KPC.h>
#include <NeKit/ErrorOr.h>
#include <NeKit/KernelPanic.h>
#include <NeKit/Utils.h>
namespace Kernel::Ext2 {
/// @brief Context for an EXT2 filesystem on a given drive
struct Ext2Context {
Kernel::DriveTrait* drive{nullptr};
EXT2_SUPER_BLOCK* superblock{nullptr};
/// @brief context with a drive
Ext2Context(Kernel::DriveTrait* drv) : drive(drv) {}
/// @brief Clean up
~Ext2Context() {
if (superblock) {
Kernel::mm_free_ptr(superblock);
superblock = nullptr;
}
}
Ext2Context(const Ext2Context&) = delete;
Ext2Context& operator=(const Ext2Context&) = delete;
Ext2Context(Ext2Context&& other) noexcept : drive(other.drive), superblock(other.superblock) {
other.drive = nullptr;
other.superblock = nullptr;
}
Ext2Context& operator=(Ext2Context&& other) noexcept {
if (this != &other) {
if (superblock) {
Kernel::mm_free_ptr(superblock);
}
drive = other.drive;
superblock = other.superblock;
other.drive = nullptr;
other.superblock = nullptr;
}
return *this;
}
Kernel::SizeT LeakBlockSize() const {
if (!superblock) return kExt2FSBlockSizeBase;
return kExt2FSBlockSizeBase << superblock->fLogBlockSize;
}
BOOL operator bool() { return superblock != nullptr; }
};
/// ======================================================================= ///
/// IFS FUNCTIONS
/// ======================================================================= ///
inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* buffer,
Kernel::UInt32 size) {
if (!drv || !buffer) return false;
Kernel::DriveTrait::DrivePacket pkt{};
pkt.fPacketContent = buffer;
pkt.fPacketSize = size;
pkt.fPacketLba = lba;
drv->fInput(pkt);
return pkt.fPacketGood;
}
inline bool ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const void* buffer,
Kernel::UInt32 size) {
if (!drv || !buffer) return false;
Kernel::DriveTrait::DrivePacket pkt{};
pkt.fPacketContent = const_cast<void*>(buffer);
pkt.fPacketSize = size;
pkt.fPacketLba = lba;
drv->fOutput(pkt);
return pkt.fPacketGood;
}
inline Kernel::ErrorOr<EXT2_SUPER_BLOCK*> ext2_load_superblock(Ext2Context* ctx) {
if (!ctx || !ctx->drive) return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorInvalidData);
auto buf = Kernel::mm_alloc_ptr(sizeof(EXT2_SUPER_BLOCK), true, false);
if (!buf) return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorHeapOutOfMemory);
Kernel::UInt32 blockLba = kExt2FSSuperblockOffset / ctx->drive->fSectorSz;
if (!ext2_read_block(ctx->drive, blockLba, buf, sizeof(EXT2_SUPER_BLOCK))) {
Kernel::mm_free_ptr(buf);
return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorDisk);
}
auto sb = reinterpret_cast<EXT2_SUPER_BLOCK*>(buf);
if (sb->fMagic != kExt2FSMagic) {
Kernel::mm_free_ptr(buf);
return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorInvalidData);
}
ctx->superblock = sb;
return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(sb);
}
// Load inode
inline Kernel::ErrorOr<Ext2Node*> ext2_load_inode(Ext2Context* ctx, Kernel::UInt32 inodeNumber) {
if (!ctx || !ctx->superblock) return Kernel::ErrorOr<Ext2Node*>(Kernel::kErrorInvalidData);
auto nodePtr = Kernel::mm_alloc_ptr(sizeof(Ext2Node), true, false);
if (!nodePtr) return Kernel::ErrorOr<Ext2Node*>(Kernel::kErrorHeapOutOfMemory);
auto ext2Node = reinterpret_cast<Ext2Node*>(nodePtr);
ext2Node->inodeNumber = inodeNumber;
// Compute block group and index within group
Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup;
Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup;
Kernel::UInt32 index = (inodeNumber - 1) % inodesPerGroup;
// dummy: just offset first inode
Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group;
if (!ext2_read_block(ctx->drive, inodeTableBlock, &ext2Node->inode, sizeof(EXT2_INODE))) {
Kernel::mm_free_ptr(nodePtr);
return Kernel::ErrorOr<Ext2Node*>(Kernel::kErrorDisk);
}
ext2Node->cursor = 0;
return Kernel::ErrorOr<Ext2Node*>(ext2Node);
}
inline Kernel::UInt32 inode_offset(const Ext2Context* ctx, Kernel::UInt32 inodeNumber) {
if (!ctx || !ctx->superblock) return 0;
return ((inodeNumber - 1) % ctx->superblock->fInodesPerGroup) * ctx->superblock->fInodeSize;
}
} // namespace Kernel::Ext2
|