1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 02:57:44 +00:00

Integrate ext2 from VFS into Kernel.

This commit is contained in:
Andreas Kling 2018-10-17 10:55:43 +02:00
parent aec8ab0a60
commit 9171521752
45 changed files with 662 additions and 1085 deletions

54
Kernel/.bochsrc Normal file
View file

@ -0,0 +1,54 @@
# configuration file generated by Bochs
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true
config_interface: textconfig
display_library: x
memory: host=32, guest=32
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest"
boot: floppy
floppy_bootsig_check: disabled=0
floppya: type=1_44, 1_44=".floppy-image", status=inserted, write_protected=0
# no floppyb
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="_fs_contents", mode=flat, cylinders=0, heads=0, spt=0, model="Generic 1234", biosdetect=auto, translation=auto
ata0-slave: type=none
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=false
ata3: enabled=false
optromimage1: file=none
optromimage2: file=none
optromimage3: file=none
optromimage4: file=none
optramimage1: file=none
optramimage2: file=none
optramimage3: file=none
optramimage4: file=none
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5, realtime=1
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU "
cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true
cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false
cpuid: smap=false, mwait=true
print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
log: -
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
speaker: enabled=true, mode=system
parport1: enabled=true, file=none
parport2: enabled=false
com1: enabled=true, mode=null
com2: enabled=false
com3: enabled=false
com4: enabled=false

View file

@ -4,42 +4,57 @@
[bits 16]
boot:
push cs
pop ds
xor bx, bx
mov ah, 0x0e
mov si, message
cli
mov ax, 0x8000
mov ss, ax
mov sp, 0xffff
push cs
pop ds
xor bx, bx
mov ah, 0x0e
mov si, message
lodsb
.lewp:
int 0x10
int 0x10
lodsb
cmp al, 0
jne .lewp
cmp al, 0
jne .lewp
mov bx, 0x1000
mov es, bx
xor bx, bx ; Load kernel @ 0x10000
mov bx, 0x1000
mov es, bx
xor bx, bx ; Load kernel @ 0x10000
mov cx, word [cur_lba]
.sector_loop:
call convert_lba_to_chs
mov ah, 0x02 ; cmd 0x02 - Read Disk Sectors
mov al, 72 ; 72 sectors (max allowed by bochs BIOS)
mov ch, 0 ; track 0
mov cl, 10 ; sector 10
mov dh, 0 ; head 0
mov al, 1 ; 1 sector at a time
mov dl, 0 ; drive 0 (fd0)
int 0x13
jc fug
mov ah, 0x02
mov al, 32
add bx, 0x9000
mov ch, 2
mov cl, 10
mov dh, 0
mov dl, 0
int 0x13
mov ah, 0x0e
mov al, '.'
int 0x10
jc fug
inc word [cur_lba]
mov cx, word [cur_lba]
cmp cx, 300
jz .sector_loop_end
mov bx, es
add bx, 0x20
mov es, bx
xor bx, bx
jmp .sector_loop
.sector_loop_end:
call durk
lgdt [cs:test_gdt_ptr]
@ -49,6 +64,20 @@ boot:
jmp 0x08:pmode
durk:
push cs
pop ds
xor bx, bx
mov ah, 0x0e
mov si, msg_sectors_loaded
lodsb
.lewp:
int 0x10
lodsb
cmp al, 0
jne .lewp
ret
pmode:
[bits 32]
mov ax, 0x10
@ -60,6 +89,14 @@ pmode:
mov ss, ax
mov esp, 0x2000
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
xor ebp, ebp
xor esi, esi
xor edi, edi
jmp 0x10000
hlt
@ -95,7 +132,56 @@ fug:
cli
hlt
; Input:
;
; AX = LBA
;
; Output:
;
; CX and DH = C/H/S address formatted for Int13,2
; CL = sector (LBA % sectors_per_track) + 1
;
; 1.44M floppy stats:
; (sectors_per_track: 18)
; (heads: 2)
; (sectors: 2880)
convert_lba_to_chs:
mov ax, cx
; AX = LBA/spt, DX = LBA%spt
xor dx, dx
div word [sectors_per_track]
; CL = sector (LBA % sectors_per_track) + 1
mov cl, dl
inc cl
; CH = track (LBA / sectors_per_track) / heads
mov ch, al
shr ch, 1
; AX = (LBA/spt)/heads, DX = (LBA/spt)%heads
xor dx, dx
div word [heads]
; DH = sector (LBA / sectors_per_track) % heads
mov dh, dl
ret
cur_lba:
dw 9
sectors_per_track:
dw 18
heads:
dw 2
msg_sectors_loaded:
db "sectors loaded", 0x0d, 0x0a, 0
message:
db "boot!", 0x0d, 0x0a, 0

View file

@ -1,74 +0,0 @@
#pragma once
#include "ext2fs.h"
#include "OwnPtr.h"
#include "DataBuffer.h"
#include "FileSystem.h"
static const size_t bytesPerSector = 512;
class Ext2VirtualNode;
class Ext2FileSystem {
public:
Ext2FileSystem() { }
~Ext2FileSystem();
void initialize();
RefPtr<DataBuffer> loadFile(ext2_dir_entry*);
ext2_inode* findInode(DWORD index);
ext2_inode* findPath(const String& path, DWORD& inodeIndex);
private:
friend class Ext2VirtualNode;
void readSuperBlock();
void readBlockGroup(DWORD);
void readInodeTable(DWORD);
void dumpDirectory(ext2_inode&);
void dumpFile(ext2_inode&);
template<typename F> void forEachBlockIn(ext2_inode&, F func);
template<typename F> void traverseDirectory(ext2_dir_entry&, DWORD blockCount, F);
template<typename F> void traverseDirectory(ext2_inode&, F);
RefPtr<DataBuffer> readFile(ext2_inode&);
RefPtr<DataBuffer> readBlocks(DWORD blockIndex, BYTE count);
void readDiskSector(DWORD sectorIndex, BYTE* buffer);
size_t blockSize() const { return 1024 << m_superBlock->s_log_frag_size; }
size_t sectorsPerBlock() const { return blockSize() / bytesPerSector; }
DWORD blockGroupForInode(DWORD inode) const;
DWORD toInodeTableIndex(DWORD inode) const;
ext2_super_block& superBlock() { ASSERT(m_superBlock); return *m_superBlock; }
OwnPtr<ext2_super_block> m_superBlock;
ext2_inode* m_root { nullptr }; // raw pointer into one of the m_inodeTables
size_t m_blockGroupCount { 0 };
ext2_group_descriptor* m_groupTable { nullptr };
ext2_inode** m_inodeTables { nullptr };
};
class Ext2VirtualNode final : public FileSystem::VirtualNode {
public:
static RefPtr<Ext2VirtualNode> create(DWORD index, String&& path, Ext2FileSystem&, DWORD inodeNumber);
virtual ~Ext2VirtualNode();
virtual size_t size() const override { return m_inode.i_size; }
virtual uid_t uid() const override { return m_inode.i_uid; }
virtual gid_t gid() const override { return m_inode.i_gid; }
virtual size_t mode() const override { return m_inode.i_mode; }
virtual size_t read(BYTE* outbuf, size_t start, size_t maxLength) override;
private:
Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem&, ext2_inode&, DWORD inodeNumber);
Ext2FileSystem& m_fileSystem;
ext2_inode& m_inode;
DWORD m_inodeNumber { 0 };
};

View file

@ -3,9 +3,9 @@
#include "Assertions.h"
#include "types.h"
template<typename T> class DoublyLinkedListNode {
template<typename T> class InlineLinkedListNode {
public:
DoublyLinkedListNode();
InlineLinkedListNode();
void setPrev(T*);
void setNext(T*);
@ -14,35 +14,35 @@ public:
T* next() const;
};
template<typename T> inline DoublyLinkedListNode<T>::DoublyLinkedListNode()
template<typename T> inline InlineLinkedListNode<T>::InlineLinkedListNode()
{
setPrev(0);
setNext(0);
}
template<typename T> inline void DoublyLinkedListNode<T>::setPrev(T* prev)
template<typename T> inline void InlineLinkedListNode<T>::setPrev(T* prev)
{
static_cast<T*>(this)->m_prev = prev;
}
template<typename T> inline void DoublyLinkedListNode<T>::setNext(T* next)
template<typename T> inline void InlineLinkedListNode<T>::setNext(T* next)
{
static_cast<T*>(this)->m_next = next;
}
template<typename T> inline T* DoublyLinkedListNode<T>::prev() const
template<typename T> inline T* InlineLinkedListNode<T>::prev() const
{
return static_cast<const T*>(this)->m_prev;
}
template<typename T> inline T* DoublyLinkedListNode<T>::next() const
template<typename T> inline T* InlineLinkedListNode<T>::next() const
{
return static_cast<const T*>(this)->m_next;
}
template<typename T> class DoublyLinkedList {
template<typename T> class InlineLinkedList {
public:
DoublyLinkedList() { }
InlineLinkedList() { }
bool isEmpty() const { return !m_head; }
size_t size() const;
@ -56,14 +56,14 @@ public:
void prepend(T*);
void append(T*);
void remove(T*);
void append(DoublyLinkedList<T>&);
void append(InlineLinkedList<T>&);
private:
T* m_head { nullptr };
T* m_tail { nullptr };
};
template<typename T> inline size_t DoublyLinkedList<T>::size() const
template<typename T> inline size_t InlineLinkedList<T>::size() const
{
size_t size = 0;
for (T* node = m_head; node; node = node->next())
@ -71,13 +71,13 @@ template<typename T> inline size_t DoublyLinkedList<T>::size() const
return size;
}
template<typename T> inline void DoublyLinkedList<T>::clear()
template<typename T> inline void InlineLinkedList<T>::clear()
{
m_head = 0;
m_tail = 0;
}
template<typename T> inline void DoublyLinkedList<T>::prepend(T* node)
template<typename T> inline void InlineLinkedList<T>::prepend(T* node)
{
if (!m_head) {
ASSERT(!m_tail);
@ -95,7 +95,7 @@ template<typename T> inline void DoublyLinkedList<T>::prepend(T* node)
m_head = node;
}
template<typename T> inline void DoublyLinkedList<T>::append(T* node)
template<typename T> inline void InlineLinkedList<T>::append(T* node)
{
if (!m_tail) {
ASSERT(!m_head);
@ -113,7 +113,7 @@ template<typename T> inline void DoublyLinkedList<T>::append(T* node)
m_tail = node;
}
template<typename T> inline void DoublyLinkedList<T>::remove(T* node)
template<typename T> inline void InlineLinkedList<T>::remove(T* node)
{
if (node->prev()) {
ASSERT(node != m_head);
@ -132,7 +132,7 @@ template<typename T> inline void DoublyLinkedList<T>::remove(T* node)
}
}
template<typename T> inline T* DoublyLinkedList<T>::removeHead()
template<typename T> inline T* InlineLinkedList<T>::removeHead()
{
T* node = head();
if (node)
@ -140,7 +140,7 @@ template<typename T> inline T* DoublyLinkedList<T>::removeHead()
return node;
}
template<typename T> inline void DoublyLinkedList<T>::append(DoublyLinkedList<T>& other)
template<typename T> inline void InlineLinkedList<T>::append(InlineLinkedList<T>& other)
{
if (!other.head())
return;

View file

@ -14,10 +14,8 @@ KERNEL_OBJS = \
PIC.o \
Syscall.o \
DataBuffer.o \
String.o \
panel.o \
Disk.o \
fs.o \
Userspace.o \
IDEDiskDevice.o
@ -27,9 +25,16 @@ VFS_OBJS = \
../VirtualFileSystem/NullDevice.o \
../VirtualFileSystem/FullDevice.o \
../VirtualFileSystem/ZeroDevice.o \
../VirtualFileSystem/RandomDevice.o
../VirtualFileSystem/RandomDevice.o \
../VirtualFileSystem/FileSystem.o \
../VirtualFileSystem/DiskBackedFileSystem.o \
../VirtualFileSystem/Ext2FileSystem.o
OBJS = $(KERNEL_OBJS) $(VFS_OBJS)
AK_OBJS = \
../AK/String.o \
../AK/StringImpl.o
OBJS = $(KERNEL_OBJS) $(VFS_OBJS) $(AK_OBJS)
NASM = nasm
KERNEL = kernel

View file

@ -1,78 +0,0 @@
#pragma once
#include "types.h"
template<typename T>
class OwnPtr {
public:
OwnPtr() { }
explicit OwnPtr(T* ptr) : m_ptr(ptr) { }
OwnPtr(OwnPtr&& other) : m_ptr(other.leakPtr()) { }
template<typename U> OwnPtr(OwnPtr<U>&& other) : m_ptr(static_cast<T*>(other.leakPtr())) { }
~OwnPtr() { clear(); }
OwnPtr& operator=(OwnPtr&& other)
{
if (this != &other) {
delete m_ptr;
m_ptr = other.leakPtr();
}
return *this;
}
template<typename U>
OwnPtr& operator=(OwnPtr<U>&& other)
{
if (this != static_cast<void*>(&other)) {
delete m_ptr;
m_ptr = other.leakPtr();
}
return *this;
}
OwnPtr& operator=(T* ptr)
{
if (m_ptr != ptr)
delete m_ptr;
m_ptr = ptr;
return *this;
}
void clear()
{
delete m_ptr;
m_ptr = nullptr;
}
bool operator!() const { return !m_ptr; }
typedef T* OwnPtr::*UnspecifiedBoolType;
operator UnspecifiedBoolType() const { return m_ptr ? &OwnPtr::m_ptr : nullptr; }
T* leakPtr()
{
T* leakedPtr = m_ptr;
m_ptr = nullptr;
return leakedPtr;
}
T* ptr() { return m_ptr; }
const T* ptr() const { return m_ptr; }
T* operator->() { return m_ptr; }
const T* operator->() const { return m_ptr; }
T& operator*() { return *m_ptr; }
const T& operator*() const { return *m_ptr; }
operator bool() { return !!m_ptr; }
private:
T* m_ptr = nullptr;
};
template<class T, class... Args> inline OwnPtr<T>
make(Args&&... args)
{
return OwnPtr<T>(new T(forward<Args>(args)...));
}

View file

@ -49,6 +49,15 @@ char* strdup(const char *str)
return s;
}
int memcmp(const void* v1, const void* v2, size_t n)
{
size_t m;
const char* s1 = (const char*)v1;
const char* s2 = (const char*)v2;
for (m = 0; m < n && *s1 == *s2; ++s1, ++s2);
return m == n ? 0 : -1;
}
extern "C" void __cxa_pure_virtual()
{
ASSERT_NOT_REACHED();

View file

@ -2,9 +2,10 @@
#include "types.h"
void memcpy(void *, const void *, DWORD);
void strcpy(char *, const char *);
int strcmp(char const *, const char *);
DWORD strlen(const char *) PURE;
void *memset(void *, BYTE, DWORD);
char *strdup(const char *);
void memcpy(void*, const void*, DWORD);
void strcpy(char*, const char*);
int strcmp(char const*, const char*);
DWORD strlen(const char*);
void *memset(void*, BYTE, DWORD);
char *strdup(const char*);
int memcmp(const void*, const void*, size_t);

View file

@ -1,86 +0,0 @@
#include "String.h"
#include <AK/StdLib.h>
String::String()
{
}
String::String(const char* characters)
: m_data(DataBuffer::copy((const BYTE*)characters, strlen(characters) + 1))
{
}
String::String(const char* characters, size_t length)
: m_data(DataBuffer::createUninitialized(length + 1))
{
memcpy(m_data->data(), characters, length);
m_data->data()[length] = '\0';
}
String::String(String&& other)
: m_data(move(other.m_data))
{
}
String::String(const String& other)
: m_data(other.m_data)
{
}
String& String::operator=(const String& other)
{
if (this == &other)
return *this;
m_data = other.m_data;
return *this;
}
String& String::operator=(const String&& other)
{
if (this == &other)
return *this;
m_data = move(other.m_data);
return *this;
}
String::~String()
{
}
bool String::operator==(const String& other) const
{
if (length() != other.length())
return false;
return strcmp(characters(), other.characters()) == 0;
}
String String::substring(size_t start, size_t length) const
{
ASSERT(start + length <= m_data->length());
// FIXME: This needs some input bounds checking.
auto buffer = DataBuffer::createUninitialized(length + 1);
memcpy(buffer->data(), characters() + start, length);
buffer->data()[length] = '\0';
String s;
s.m_data = move(buffer);
return s;
}
Vector<String> String::split(char separator) const
{
Vector<String> v;
size_t substart = 0;
for (size_t i = 0; i < length(); ++i) {
char ch = characters()[i];
if (ch == separator) {
size_t sublen = i - substart;
if (sublen != 0)
v.append(substring(substart, sublen));
substart = i + 1;
}
}
size_t taillen = length() - 1 - substart;
if (taillen != 0)
v.append(substring(substart, taillen));
return v;
}

View file

@ -1,31 +0,0 @@
#pragma once
#include "DataBuffer.h"
#include "Vector.h"
class String {
public:
String();
String(const char* characters);
String(const char* characters, size_t length);
String(String&&);
String(const String&);
String& operator=(const String&);
String& operator=(const String&&);
~String();
bool isEmpty() const { return m_data ? m_data->isEmpty() : true; }
size_t length() const { return m_data ? m_data->length() : 0; }
char operator[](size_t i) const { return (char)m_data->data()[i]; }
const char* characters() const { return m_data ? (const char*)m_data->data() : nullptr; }
bool operator==(const String&) const;
Vector<String> split(char separator) const;
String substring(size_t start, size_t length) const;
private:
RefPtr<DataBuffer> m_data;
};

View file

@ -11,7 +11,7 @@ Task* current;
Task* s_kernelTask;
static pid_t next_pid;
static DoublyLinkedList<Task>* s_tasks;
static InlineLinkedList<Task>* s_tasks;
static bool contextSwitch(Task*);
@ -46,7 +46,7 @@ void Task::initialize()
{
current = nullptr;
next_pid = 0;
s_tasks = new DoublyLinkedList<Task>;
s_tasks = new InlineLinkedList<Task>;
s_kernelTask = new Task(0, "idle", IPC::Handle::Any, Task::Ring0);
redoKernelTaskTSS();
loadTaskRegister(s_kernelTask->selector());
@ -415,12 +415,14 @@ int Task::sys$open(const char* path, size_t pathLength)
FileHandle* Task::openFile(String&& path)
{
#if 0
auto vnode = FileSystem::createVirtualNode(move(path));
if (!vnode) {
kprintf("createVirtualNode failed\n");
return nullptr;
}
#if 1
#endif
#if 0
FileHandle* fh = new FileHandle;
kprintf("made new FileHandle\n");
fh->m_fd = m_fileHandles.size();

View file

@ -2,18 +2,18 @@
#include "types.h"
#include "IPC.h"
#include "DoublyLinkedList.h"
#include "String.h"
#include "InlineLinkedList.h"
#include <AK/String.h>
#include "TSS.h"
#include "Vector.h"
#include <AK/Vector.h>
#include "i386.h"
//#define TASK_SANITY_CHECKS
class FileHandle;
class Task : public DoublyLinkedListNode<Task> {
friend class DoublyLinkedListNode<Task>;
class Task : public InlineLinkedListNode<Task> {
friend class InlineLinkedListNode<Task>;
public:
#ifdef TASK_SANITY_CHECKS
static void checkSanity(const char* msg = nullptr);

View file

@ -1,90 +0,0 @@
#pragma once
#include "Assertions.h"
#include "kmalloc.h"
#define SANITIZE_VECTOR
template<typename T>
class Vector {
public:
Vector() { }
~Vector();
Vector(const Vector&&);
Vector& operator=(const Vector&&);
bool isEmpty() const { return m_size == 0; }
size_t size() const { return m_size; }
size_t capacity() const { return m_capacity; }
void append(T&&);
void clear();
const T& operator[](size_t i) const { return m_elements[i]; }
T& operator[](size_t i) { return m_elements[i]; }
private:
Vector(const Vector&) = delete;
Vector& operator=(const Vector&) = delete;
void ensureCapacity(size_t);
T* m_elements { nullptr };
size_t m_size { 0 };
size_t m_capacity { 0 };
};
template<typename T>
Vector<T>::~Vector()
{
clear();
#ifdef SANITIZE_VECTOR
m_elements = (T*)0xdddddddd;
m_size = 0x8a8a8a8a;
m_capacity = 0xa8a8a8a8;
#endif
}
template<typename T>
void Vector<T>::clear()
{
if (!m_elements)
return;
for (size_t i = 0; i < m_size; ++i) {
m_elements[i].~T();
}
kfree(m_elements);
m_elements = nullptr;
m_size = 0;
m_capacity = 0;
}
template<typename T>
void Vector<T>::append(T&& element)
{
ensureCapacity(m_size + 1);
new (&m_elements[m_size]) T(move(element));
++m_size;
}
template<typename T>
void Vector<T>::ensureCapacity(size_t neededCapacity)
{
if (neededCapacity <= m_capacity)
return;
size_t newCapacity = (neededCapacity + 8) & ~7;
// FIXME: We need to support further growth here, jeez...
ASSERT(m_capacity == 0);
ASSERT(!m_elements);
m_capacity = newCapacity;
T* newElements = (T*)kmalloc(m_capacity * sizeof(T));
#ifdef SANITIZE_VECTOR
memset(newElements, 0x66, m_capacity * sizeof(T));
#endif
if (m_elements) {
memcpy(newElements, m_elements, m_size * sizeof(T));
kfree(m_elements);
}
m_elements = newElements;
}

View file

@ -1,451 +0,0 @@
#include "Disk.h"
#include "Task.h"
#include "VGA.h"
#include "kmalloc.h"
#include "Ext2FileSystem.h"
#include "i386.h"
#include "StdLib.h"
#include "OwnPtr.h"
#include "FileSystem.h"
#include "String.h"
//#define FS_DEBUG
Ext2FileSystem::~Ext2FileSystem()
{
kprintf("fs: kill Ext2FileSystem\n");
ASSERT(false);
kfree(m_groupTable);
m_groupTable = nullptr;
if (m_inodeTables) {
for (DWORD i = 0; i < m_blockGroupCount; ++i)
delete [] m_inodeTables[i];
}
delete [] m_inodeTables;
m_inodeTables = nullptr;
}
static Ext2FileSystem* fileSystem;
void Ext2FileSystem::readDiskSector(DWORD sectorIndex, BYTE* buffer)
{
Task::checkSanity("Ext2FileSystem::readDiskSector");
bool success = Disk::readSectors(sectorIndex, 1, buffer);
(void) success;
}
RefPtr<DataBuffer> Ext2FileSystem::readBlocks(DWORD blockIndex, BYTE count)
{
Task::checkSanity("readBlocks");
if (!m_superBlock) {
kprintf("fs: Attempt to read blocks without superblock!\n");
HANG;
}
#ifdef FS_DEBUG
kprintf("Read %u block(s) starting at %u\n", count, blockIndex);
#endif
// FIXME: This is broken up into 1-sector reads because the disk task can't handle multi-sector reads yet.
auto buffer = DataBuffer::createUninitialized(count * sectorsPerBlock() * bytesPerSector);
BYTE* bufptr = (BYTE*)buffer->data();
for (DWORD i = 0; i < count; ++i) {
readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 0, bufptr);
readDiskSector(((blockIndex + i) * sectorsPerBlock()) + 1, bufptr + bytesPerSector);
bufptr += bytesPerSector * 2;
}
return buffer;
}
void Ext2FileSystem::readSuperBlock()
{
ASSERT(!m_superBlock);
ASSERT(!m_groupTable);
m_superBlock = make<ext2_super_block>();
readDiskSector(2, (BYTE*)m_superBlock.ptr());
if (m_superBlock->s_magic != EXT2_MAGIC) {
kprintf("fs: PANIC! No ext2 filesystem found\n");
HANG;
}
kprintf("fs: ext2 filesystem found -- %u inodes, %u blocks\n",
m_superBlock->s_inodes_count,
m_superBlock->s_blocks_count);
const BYTE* u = m_superBlock->s_uuid;
kprintf("fs: UUID: %b%b%b%b-%b%b-%b%b-%b%b-%b%b%b%b%b%b\n",
u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16]);
#ifdef FS_DEBUG
kprintf("fs: Block size is %u bytes\n", 1024 << m_superBlock->s_log_frag_size);
kprintf("fs: Blocks per group: %u\n", m_superBlock->s_blocks_per_group);
#endif
m_blockGroupCount = m_superBlock->s_blocks_count / m_superBlock->s_blocks_per_group;
if ((m_superBlock->s_blocks_count % m_superBlock->s_blocks_per_group) != 0)
++m_blockGroupCount;
m_inodeTables = new ext2_inode*[m_blockGroupCount];
memset(m_inodeTables, 0, sizeof(ext2_inode*) * m_blockGroupCount);
}
void Ext2FileSystem::readBlockGroup(DWORD index)
{
Task::checkSanity("readBlockGroup");
DWORD superBlockBI = m_superBlock->s_first_data_block + (m_superBlock->s_blocks_per_group * index);
DWORD descriptorTableBI = superBlockBI + 1;
//DWORD blockBitmapBI = descriptorTableBI + 1;
//DWORD inodeBitmapBI = blockBitmapBI + 1;
//DWORD inodeTableBI = inodeBitmapBI + 1;
auto buffer = readBlocks(descriptorTableBI, 1);
#ifdef FS_DEBUG
kprintf("Inodes per group = %u\n", superBlock().s_inodes_per_group);
kprintf("First data block = %u\n", superBlock().s_first_data_block);
#endif
m_groupTable = (ext2_group_descriptor*)kmalloc(blockSize());
memcpy(m_groupTable, buffer->data(), buffer->length());
#ifdef FS_DEBUG
kprintf("[%u] block bitmap: %u\n", index, m_groupTable[index].bg_block_bitmap);
kprintf("[%u] inode bitmap: %u\n", index, m_groupTable[index].bg_inode_bitmap);
kprintf("[%u] inode table: %u\n", index, m_groupTable[index].bg_inode_table);
#endif
}
template<typename F>
void Ext2FileSystem::traverseDirectory(ext2_dir_entry& firstEntry, DWORD blockCount, F func)
{
Task::checkSanity("traverseDirectory1");
auto* entry = &firstEntry;
char* name = new char[EXT2_NAME_LEN + 1];
auto* end = (ext2_dir_entry*)((BYTE*)entry + blockCount * blockSize());
while (entry < end) {
if (entry->d_inode != 0) {
memcpy(name, entry->d_name, entry->d_name_len);
name[entry->d_name_len] = 0;
func(name, *entry);
}
entry = (ext2_dir_entry*)((BYTE *)entry + entry->d_rec_len);
}
delete [] name;
}
void Ext2FileSystem::readInodeTable(DWORD blockGroup)
{
Task::checkSanity("readInodeTable");
ext2_inode*& inodeTable = m_inodeTables[blockGroup];
if (!inodeTable)
inodeTable = new ext2_inode[m_superBlock->s_inodes_per_group];
DWORD inodeTableBlocks = (m_superBlock->s_inodes_per_group * sizeof(ext2_inode)) / blockSize();
// kprintf("inode table blocks: %u\n", inodeTableBlocks);
auto buffer = readBlocks(m_groupTable[blockGroup].bg_inode_table, inodeTableBlocks);
memcpy(inodeTable, buffer->data(), m_superBlock->s_inodes_per_group * sizeof(ext2_inode));
m_root = &inodeTable[1];
#ifdef FS_DEBUG
kprintf("Root directory inode:\n");
kprintf("sizeof(ext2_inode): %u\n", sizeof(ext2_inode));
kprintf("sizeof(ext2_dir_entry): %u\n", sizeof(ext2_dir_entry));
kprintf("Mode: %u, Owner: %u/%u, Size: %u\n", m_root->i_mode, m_root->i_uid, m_root->i_gid, m_root->i_size);
kprintf("Directory blocks: { ");
for (DWORD i = 0; i < 12; ++i) {
kprintf( "%u ", m_root->i_block[i] );
}
kprintf("}\n");
#endif
}
template<typename F>
void Ext2FileSystem::forEachBlockIn(ext2_inode& inode, F func)
{
Task::checkSanity("forEachBlockIn");
DWORD blockCount = inode.i_blocks / (2 << m_superBlock->s_log_block_size);
// FIXME: Support indirect blocks
for (DWORD i = 0; i < blockCount; ++i) {
//kprintf(" [blk %u]\n", inode.i_block[i]);
auto buffer = readBlocks(inode.i_block[i], 1);
func(move(buffer));
}
}
DWORD Ext2FileSystem::blockGroupForInode(DWORD inode) const
{
// FIXME: Implement
(void)inode;
return 0;
}
DWORD Ext2FileSystem::toInodeTableIndex(DWORD inode) const
{
// FIXME: Implement
return inode - 1;
}
ext2_inode* Ext2FileSystem::findInode(DWORD index)
{
if (index >= m_superBlock->s_inodes_count)
return nullptr;
return &m_inodeTables[blockGroupForInode(index)][toInodeTableIndex(index)];
}
ext2_inode* Ext2FileSystem::findPath(const String& path, DWORD& inodeIndex)
{
Task::checkSanity("findPath entry");
ASSERT(m_root);
Task::checkSanity("findPath entry2");
if (path.isEmpty())
return nullptr;
if (path[0] != '/')
return nullptr;
ext2_inode* dir = m_root;
Task::checkSanity("findPath pre-vector");
Vector<String> pathParts = path.split('/');
Task::checkSanity("findPath post-split");
for (size_t i = 0; i < pathParts.size(); ++i) {
//kprintf("[%u] %s\n", i, pathParts[i].characters());
auto& part = pathParts[i];
bool foundPart = false;
//kprintf("looking for part '%s' in inode{%p}\n", part.characters(), dir);
traverseDirectory(*dir, [&] (const char* name, ext2_dir_entry& entry) {
//kprintf(" ?= %s\n", name);
if (String(name) == part) {
foundPart = true;
//kprintf("found part ==> inode %u (type %b)\n", entry.d_inode, entry.d_file_type);
dir = findInode(entry.d_inode);
inodeIndex = entry.d_inode;
// FIXME: don't try to traverse files as if they're directories
// FIXME: need a way to skip the remaining traverseDirectory() callbacks
}
});
if (!foundPart)
return nullptr;
}
return dir;
}
template<typename F>
void Ext2FileSystem::traverseDirectory(ext2_inode& inode, F func)
{
Task::checkSanity("traverseDirectory2");
//kprintf("in traverseDir\n");
forEachBlockIn(inode, [this, &func] (RefPtr<DataBuffer>&& data) {
auto* directory = (ext2_dir_entry*)data->data();
traverseDirectory<F>(*directory, 1, func);
});
//kprintf("out traverseDir\n");
}
RefPtr<DataBuffer> Ext2FileSystem::readFile(ext2_inode& inode)
{
auto buffer = DataBuffer::createUninitialized(inode.i_size + 1);
BYTE* bufptr = buffer->data();
size_t dataRemaining = inode.i_size;
forEachBlockIn(inode, [this, &bufptr, &dataRemaining] (RefPtr<DataBuffer>&& data) {
memcpy(bufptr, data->data(), min(dataRemaining, data->length()));
dataRemaining -= blockSize();
bufptr += blockSize();
});
// HACK: This is silly, but let's just null terminate here for comfort.
buffer->data()[buffer->length() - 1] = '\0';
return buffer;
}
void Ext2FileSystem::dumpFile(ext2_inode& inode)
{
auto buffer = readFile(inode);
kprintf("%s", buffer->data());
}
void Ext2FileSystem::dumpDirectory(ext2_inode& inode)
{
traverseDirectory(inode, [this] (const char* name, ext2_dir_entry& entry) {
bool isDirectory = entry.d_file_type == EXT2_FT_DIR;
ext2_inode& inode = m_inodeTables[blockGroupForInode(entry.d_inode)][toInodeTableIndex(entry.d_inode)];
kprintf("i:%x %b %u:%u %x %s%s\n",
entry.d_inode,
entry.d_file_type,
inode.i_uid,
inode.i_gid,
inode.i_size,
name,
isDirectory ? "/" : "");
});
}
#if 0
void Ext2FileSystem::readRoot()
{
auto buffer = readBlocks(m_root->i_block[0], 1);
auto* dir_block = (ext2_dir_entry*)buffer.data();
traverseDirectory(dir_block, [this] (const char* name, ext2_dir_entry* entry) {
if (!strcmp(name, "test2")) {
auto test2_entry = loadFile(entry);
new Task((void (*)())test2_entry.data(), "test2", IPC::Handle::Any, Task::Ring3);
// HACK: Don't delete the code we just started running :)
test2_entry.leak();
} else if (!strcmp( name, "motd.txt")) {
auto motd_txt = loadFile(entry);
kprintf("===============================================\n\n");
vga_set_attr(0x03);
kprintf("%s\n", motd_txt.data());
vga_set_attr(0x07);
kprintf("===============================================\n");
}
});
}
#endif
void Ext2FileSystem::initialize()
{
readSuperBlock();
readBlockGroup(0);
readInodeTable(0);
#ifdef FS_DEBUG
dumpDirectory(*m_root);
#endif
DWORD inodeIndex;
auto* file = findPath("/motd.txt", inodeIndex);
dumpFile(*file);
}
RefPtr<DataBuffer> Ext2FileSystem::loadFile(ext2_dir_entry* dirent)
{
Task::checkSanity("loadFile");
DWORD inode_group = (dirent->d_inode - 1) / superBlock().s_inodes_per_group;
#ifdef FS_DEBUG
kprintf("inode: %u (group %u)\n", dirent->d_inode, inode_group);
kprintf("inode table at block %u\n", m_groupTable[inode_group].bg_inode_table);
#endif
// Calculate interesting offset into inode block.
DWORD inode_index = (dirent->d_inode - 1) % superBlock().s_inodes_per_group;
// Load the relevant inode block.
auto buffer = readBlocks(m_groupTable[inode_group].bg_inode_table, 4);
auto* inode_table = (ext2_inode*)buffer->data();
#ifdef FS_DEBUG
kprintf("inode index: %u\n", inode_index);
#endif
ext2_inode* inode = &inode_table[inode_index];
#ifdef FS_DEBUG
kprintf("Mode: %u UID: %u GID: %u Size: %u Block0: %u\n", inode->i_mode, inode->i_uid, inode->i_gid, inode->i_size, inode->i_block[0]);
#endif
auto fileContents = readBlocks(inode->i_block[0], 1);
#ifdef FS_DEBUG
kprintf("File @ %p\n", fileContents->data());
kprintf("File contents: %b %b %b %b %b\n",
(*fileContents)[0],
(*fileContents)[1],
(*fileContents)[2],
(*fileContents)[3],
(*fileContents)[4]);
#endif
return fileContents;
}
RefPtr<Ext2VirtualNode> Ext2VirtualNode::create(DWORD index, String&& path, Ext2FileSystem& fs, DWORD inodeNumber)
{
Task::checkSanity("enter E2VN::create");
ext2_inode* inode = fs.findInode(inodeNumber);
Task::checkSanity("post findInode");
if (!inode)
return nullptr;
auto* v = new Ext2VirtualNode(index, move(path), fs, *inode, inodeNumber);
kprintf("v=%p\n", v);
auto r = adoptRef(v);
kprintf("adopted(v)=%p\n", r.ptr());
return r;
}
Ext2VirtualNode::Ext2VirtualNode(DWORD index, String&& path, Ext2FileSystem& fs, ext2_inode& inode, DWORD inodeNumber)
: VirtualNode(index, move(path))
, m_fileSystem(fs)
, m_inode(inode)
, m_inodeNumber(inodeNumber)
{
Task::checkSanity("Ext2VirtualNode::Ext2VirtualNode");
}
Ext2VirtualNode::~Ext2VirtualNode()
{
Task::checkSanity("Ext2VirtualNode::~Ext2VirtualNode");
}
size_t Ext2VirtualNode::read(BYTE* outbuf, size_t start, size_t maxLength)
{
Task::checkSanity("Ext2VirtualNode::read");
kprintf("Ext2VirtualNode::read\n");
if (start >= size())
return 0;
auto fileContents = m_fileSystem.readFile(m_inode);
if (!fileContents)
return 0;
ASSERT(start < fileContents->length());
size_t nread = min(maxLength, fileContents->length() - start);
memcpy(outbuf, fileContents->data(), nread);
return nread;
}
namespace FileSystem {
static DWORD nextVNodeIndex;
void initialize()
{
nextVNodeIndex = 0;
fileSystem = new Ext2FileSystem;
fileSystem->initialize();
}
VirtualNode::VirtualNode(DWORD index, String&& path)
: m_index(index)
, m_path(move(path))
{
}
VirtualNode::~VirtualNode()
{
}
RefPtr<VirtualNode> createVirtualNode(String&& path)
{
Task::checkSanity("createVirtualNode");
DWORD inodeIndex = 0x12345678;
Task::checkSanity("pre-findPath");
kprintf("path: '%s'\n", path.characters());
auto* inode = fileSystem->findPath(path, inodeIndex);
Task::checkSanity("post-findPath");
if (!inode)
return nullptr;
kprintf("creating e2vn\n");
return Ext2VirtualNode::create(nextVNodeIndex++, move(path), *fileSystem, inodeIndex);
}
}

View file

@ -12,13 +12,13 @@
#include "StdLib.h"
#include "Syscall.h"
#include "CMOS.h"
#include "FileSystem.h"
#include "Userspace.h"
#include "IDEDiskDevice.h"
#include <VirtualFileSystem/NullDevice.h>
#include <VirtualFileSystem/ZeroDevice.h>
#include <VirtualFileSystem/FullDevice.h>
#include <VirtualFileSystem/RandomDevice.h>
#include <VirtualFileSystem/Ext2FileSystem.h>
#include <AK/OwnPtr.h>
#if 0
@ -127,7 +127,6 @@ void init()
banner();
Disk::initialize();
FileSystem::initialize();
auto dev_hd0 = IDEDiskDevice::create();
auto dev_null = make<NullDevice>();
@ -135,6 +134,9 @@ void init()
auto dev_zero = make<ZeroDevice>();
auto dev_random = make<RandomDevice>();
auto e2fs = Ext2FileSystem::create(dev_hd0.copyRef());
e2fs->initialize();
// new Task(motd_main, "motd", IPC::Handle::MotdTask, Task::Ring0);
new Task(user_main, "user", IPC::Handle::UserTask, Task::Ring3);

View file

@ -1,5 +1,5 @@
#!/bin/sh
#bochs -q -f .bochs.conf
bochs -q -f .bochsrc
qemu-system-i386 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents $@
#qemu-system-i386 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents $@