mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-26 12:41:59 +03:00
Import all this stuff into a single repo called Serenity.
This commit is contained in:
commit
5a30055157
Notes:
sideshowbarker
2024-07-19 18:51:29 +09:00
Author: https://github.com/awesomekling 🔰 Commit: https://github.com/SerenityOS/serenity/commit/5a300551574
1
AK/.gitignore
vendored
Normal file
1
AK/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
akit-test
|
15
AK/Assertions.h
Normal file
15
AK/Assertions.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define ASSERT(x) assert(x)
|
||||
#define ASSERT_NOT_REACHED() assert(false)
|
||||
|
||||
namespace AK {
|
||||
|
||||
inline void notImplemented() { assert(false); }
|
||||
|
||||
}
|
||||
|
||||
using AK::notImplemented;
|
||||
|
71
AK/Bitmap.h
Normal file
71
AK/Bitmap.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "StdLib.h"
|
||||
#include "Types.h"
|
||||
#include "kmalloc.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
// NOTE: A wrapping Bitmap won't try to free the wrapped data.
|
||||
static Bitmap wrap(byte* data, unsigned size)
|
||||
{
|
||||
return Bitmap(data, size);
|
||||
}
|
||||
|
||||
static Bitmap create(unsigned size)
|
||||
{
|
||||
return Bitmap(size);
|
||||
}
|
||||
|
||||
~Bitmap()
|
||||
{
|
||||
if (m_owned)
|
||||
kfree(m_data);
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
unsigned size() const { return m_size; }
|
||||
bool get(unsigned index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return 0 != (m_data[index / 8] & (1u << (index % 8)));
|
||||
}
|
||||
void set(unsigned index, bool value) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
if (value)
|
||||
m_data[index / 8] |= static_cast<byte>((1u << (index % 8)));
|
||||
else
|
||||
m_data[index / 8] &= static_cast<byte>(~(1u << (index % 8)));
|
||||
}
|
||||
|
||||
byte* data() { return m_data; }
|
||||
const byte* data() const { return m_data; }
|
||||
|
||||
private:
|
||||
explicit Bitmap(unsigned size)
|
||||
: m_size(size)
|
||||
, m_owned(true)
|
||||
{
|
||||
ASSERT(m_size != 0);
|
||||
m_data = reinterpret_cast<byte*>(kmalloc(ceilDiv(size, 8u)));
|
||||
}
|
||||
|
||||
Bitmap(byte* data, unsigned size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
, m_owned(false)
|
||||
{
|
||||
}
|
||||
|
||||
byte* m_data { nullptr };
|
||||
unsigned m_size { 0 };
|
||||
bool m_owned { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::Bitmap;
|
||||
|
120
AK/Buffer.h
Normal file
120
AK/Buffer.h
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "Retainable.h"
|
||||
#include "RetainPtr.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "kmalloc.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
class Buffer : public Retainable<Buffer<T>> {
|
||||
public:
|
||||
static RetainPtr<Buffer> createUninitialized(size_t count);
|
||||
static RetainPtr<Buffer> copy(const T*, size_t count);
|
||||
static RetainPtr<Buffer> wrap(T*, size_t count);
|
||||
static RetainPtr<Buffer> adopt(T*, size_t count);
|
||||
|
||||
~Buffer() { clear(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (!m_elements)
|
||||
return;
|
||||
kfree(m_elements);
|
||||
m_elements = nullptr;
|
||||
}
|
||||
|
||||
T& operator[](size_t i) { ASSERT(i < m_size); return m_elements[i]; }
|
||||
const T& operator[](size_t i) const { ASSERT(i < m_size); return m_elements[i]; }
|
||||
bool isEmpty() const { return !m_size; }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
T* pointer() { return m_elements; }
|
||||
const T* pointer() const { return m_elements; }
|
||||
|
||||
T* offsetPointer(size_t offset) { return m_elements + offset; }
|
||||
const T* offsetPointer(size_t offset) const { return m_elements + offset; }
|
||||
|
||||
const void* endPointer() const { return m_elements + m_size; }
|
||||
|
||||
// NOTE: trim() does not reallocate.
|
||||
void trim(size_t size)
|
||||
{
|
||||
ASSERT(size <= m_size);
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ConstructionMode { Uninitialized, Copy, Wrap, Adopt };
|
||||
explicit Buffer(size_t); // For ConstructionMode=Uninitialized
|
||||
Buffer(const T*, size_t, ConstructionMode); // For ConstructionMode=Copy
|
||||
Buffer(T*, size_t, ConstructionMode); // For ConstructionMode=Wrap/Adopt
|
||||
Buffer() { }
|
||||
|
||||
T* m_elements { nullptr };
|
||||
size_t m_size { 0 };
|
||||
bool m_owned { false };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline Buffer<T>::Buffer(size_t size)
|
||||
: m_size(size)
|
||||
{
|
||||
m_elements = static_cast<T*>(kmalloc(size * sizeof(T)));
|
||||
m_owned = true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline Buffer<T>::Buffer(const T* elements, size_t size, ConstructionMode mode)
|
||||
: m_size(size)
|
||||
{
|
||||
ASSERT(mode == Copy);
|
||||
m_elements = static_cast<T*>(kmalloc(size * sizeof(T)));
|
||||
memcpy(m_elements, elements, size * sizeof(T));
|
||||
m_owned = true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline Buffer<T>::Buffer(T* elements, size_t size, ConstructionMode mode)
|
||||
: m_elements(elements)
|
||||
, m_size(size)
|
||||
{
|
||||
if (mode == Adopt) {
|
||||
m_owned = true;
|
||||
} else if (mode == Wrap) {
|
||||
m_owned = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline RetainPtr<Buffer<T>> Buffer<T>::createUninitialized(size_t size)
|
||||
{
|
||||
return ::adopt(*new Buffer<T>(size));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline RetainPtr<Buffer<T>> Buffer<T>::copy(const T* elements, size_t size)
|
||||
{
|
||||
return ::adopt(*new Buffer<T>(elements, size, Copy));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline RetainPtr<Buffer<T>> Buffer<T>::wrap(T* elements, size_t size)
|
||||
{
|
||||
return ::adopt(*new Buffer<T>(elements, size, Wrap));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline RetainPtr<Buffer<T>> Buffer<T>::adopt(T* elements, size_t size)
|
||||
{
|
||||
return ::adopt(*new Buffer<T>(elements, size, Adopt));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using AK::Buffer;
|
||||
|
85
AK/ByteBuffer.h
Normal file
85
AK/ByteBuffer.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "Types.h"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace AK {
|
||||
|
||||
class ByteBuffer {
|
||||
public:
|
||||
ByteBuffer() { }
|
||||
ByteBuffer(std::nullptr_t) { }
|
||||
ByteBuffer(const ByteBuffer& other)
|
||||
: m_impl(other.m_impl.copyRef())
|
||||
{
|
||||
}
|
||||
ByteBuffer(ByteBuffer&& other)
|
||||
: m_impl(std::move(other.m_impl))
|
||||
{
|
||||
}
|
||||
ByteBuffer& operator=(ByteBuffer&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
m_impl = std::move(other.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
static ByteBuffer createUninitialized(size_t size) { return ByteBuffer(Buffer<byte>::createUninitialized(size)); }
|
||||
static ByteBuffer copy(const byte* data, size_t size) { return ByteBuffer(Buffer<byte>::copy(data, size)); }
|
||||
static ByteBuffer wrap(byte* data, size_t size) { return ByteBuffer(Buffer<byte>::wrap(data, size)); }
|
||||
static ByteBuffer adopt(byte* data, size_t size) { return ByteBuffer(Buffer<byte>::adopt(data, size)); }
|
||||
|
||||
~ByteBuffer() { clear(); }
|
||||
void clear() { m_impl = nullptr; }
|
||||
|
||||
operator bool() const { return !isNull(); }
|
||||
bool operator!() const { return isNull(); }
|
||||
bool isNull() const { return m_impl == nullptr; }
|
||||
|
||||
byte& operator[](size_t i) { ASSERT(m_impl); return (*m_impl)[i]; }
|
||||
byte operator[](size_t i) const { ASSERT(m_impl); return (*m_impl)[i]; }
|
||||
bool isEmpty() const { return !m_impl || m_impl->isEmpty(); }
|
||||
size_t size() const { return m_impl ? m_impl->size() : 0; }
|
||||
|
||||
byte* pointer() { return m_impl ? m_impl->pointer() : nullptr; }
|
||||
const byte* pointer() const { return m_impl ? m_impl->pointer() : nullptr; }
|
||||
|
||||
byte* offsetPointer(size_t offset) { return m_impl ? m_impl->offsetPointer(offset) : nullptr; }
|
||||
const byte* offsetPointer(size_t offset) const { return m_impl ? m_impl->offsetPointer(offset) : nullptr; }
|
||||
|
||||
const void* endPointer() const { return m_impl ? m_impl->endPointer() : nullptr; }
|
||||
|
||||
// NOTE: trim() does not reallocate.
|
||||
void trim(size_t size)
|
||||
{
|
||||
if (m_impl)
|
||||
m_impl->trim(size);
|
||||
}
|
||||
|
||||
ByteBuffer slice(size_t offset, size_t size) const
|
||||
{
|
||||
if (isNull())
|
||||
return { };
|
||||
if (offset >= this->size())
|
||||
return { };
|
||||
if (offset + size >= this->size())
|
||||
size = this->size() - offset;
|
||||
return copy(offsetPointer(offset), size);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ByteBuffer(RetainPtr<Buffer<byte>>&& impl)
|
||||
: m_impl(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
RetainPtr<Buffer<byte>> m_impl;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::ByteBuffer;
|
||||
|
95
AK/HashMap.h
Normal file
95
AK/HashMap.h
Normal file
@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include "HashTable.h"
|
||||
#include <utility>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename K, typename V>
|
||||
class HashMap {
|
||||
private:
|
||||
struct Entry {
|
||||
K key;
|
||||
V value;
|
||||
|
||||
bool operator==(const Entry& other)
|
||||
{
|
||||
return key == other.key;
|
||||
}
|
||||
};
|
||||
|
||||
struct EntryTraits {
|
||||
static unsigned hash(const Entry& entry) { return Traits<K>::hash(entry.key); }
|
||||
static void dump(const Entry& entry)
|
||||
{
|
||||
printf("key=");
|
||||
Traits<K>::dump(entry.key);
|
||||
printf(" value=");
|
||||
Traits<V>::dump(entry.value);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
HashMap() { }
|
||||
|
||||
HashMap(HashMap&& other)
|
||||
: m_table(std::move(other.m_table))
|
||||
{
|
||||
}
|
||||
|
||||
HashMap& operator=(HashMap&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_table = std::move(other.m_table);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isEmpty() const { return m_table.isEmpty(); }
|
||||
unsigned size() const { return m_table.size(); }
|
||||
unsigned capacity() const { return m_table.capacity(); }
|
||||
|
||||
void set(const K&, V&&);
|
||||
|
||||
typedef HashTable<Entry, EntryTraits> HashTableType;
|
||||
typedef typename HashTableType::Iterator IteratorType;
|
||||
typedef typename HashTableType::ConstIterator ConstIteratorType;
|
||||
|
||||
IteratorType begin() { return m_table.begin(); }
|
||||
IteratorType end() { return m_table.end(); }
|
||||
IteratorType find(const K&);
|
||||
|
||||
ConstIteratorType begin() const { return m_table.begin(); }
|
||||
ConstIteratorType end() const { return m_table.end(); }
|
||||
ConstIteratorType find(const K&) const;
|
||||
|
||||
void dump() const { m_table.dump(); }
|
||||
|
||||
private:
|
||||
HashTable<Entry, EntryTraits> m_table;
|
||||
};
|
||||
|
||||
template<typename K, typename V>
|
||||
void HashMap<K, V>::set(const K& key, V&& value)
|
||||
{
|
||||
m_table.set(Entry{key, std::move(value)});
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
auto HashMap<K, V>::find(const K& key) -> IteratorType
|
||||
{
|
||||
Entry dummy { key, V() };
|
||||
return m_table.find(dummy);
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
auto HashMap<K, V>::find(const K& key) const -> ConstIteratorType
|
||||
{
|
||||
Entry dummy { key, V() };
|
||||
return m_table.find(dummy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using AK::HashMap;
|
||||
|
360
AK/HashTable.h
Normal file
360
AK/HashTable.h
Normal file
@ -0,0 +1,360 @@
|
||||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "SinglyLinkedList.h"
|
||||
#include "Traits.h"
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
//#define HASHTABLE_DEBUG
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T, typename = Traits<T>> class HashTable;
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
class HashTable {
|
||||
private:
|
||||
struct Bucket {
|
||||
SinglyLinkedList<T> chain;
|
||||
};
|
||||
|
||||
public:
|
||||
HashTable() { }
|
||||
explicit HashTable(HashTable&& other)
|
||||
: m_buckets(other.m_buckets)
|
||||
, m_size(other.m_size)
|
||||
, m_capacity(other.m_capacity)
|
||||
{
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
other.m_buckets = nullptr;
|
||||
}
|
||||
HashTable& operator=(HashTable&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_buckets = other.m_buckets;
|
||||
m_size = other.m_size;
|
||||
m_capacity = other.m_capacity;
|
||||
other.m_size = 0;
|
||||
other.m_capacity = 0;
|
||||
other.m_buckets = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~HashTable() { clear(); }
|
||||
bool isEmpty() const { return !m_size; }
|
||||
unsigned size() const { return m_size; }
|
||||
unsigned capacity() const { return m_capacity; }
|
||||
|
||||
void set(T&&);
|
||||
bool contains(const T&) const;
|
||||
void clear();
|
||||
|
||||
void dump() const;
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
bool operator!=(const Iterator& other)
|
||||
{
|
||||
if (m_isEnd && other.m_isEnd)
|
||||
return false;
|
||||
return &m_table != &other.m_table
|
||||
|| m_isEnd != other.m_isEnd
|
||||
|| m_bucketIndex != other.m_bucketIndex
|
||||
|| m_bucketIterator != other.m_bucketIterator;
|
||||
}
|
||||
T& operator*()
|
||||
{
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("retrieve { bucketIndex: %u, isEnd: %u }\n", m_bucketIndex, m_isEnd);
|
||||
#endif
|
||||
return *m_bucketIterator;
|
||||
}
|
||||
Iterator& operator++()
|
||||
{
|
||||
skipToNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void skipToNext()
|
||||
{
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
unsigned pass = 0;
|
||||
#endif
|
||||
while (!m_isEnd) {
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
++pass;
|
||||
printf("skipToNext pass %u, m_bucketIndex=%u\n", pass, m_bucketIndex);
|
||||
#endif
|
||||
if (m_bucketIterator.isEnd()) {
|
||||
++m_bucketIndex;
|
||||
if (m_bucketIndex >= m_table.capacity()) {
|
||||
m_isEnd = true;
|
||||
return;
|
||||
}
|
||||
m_bucketIterator = m_table.m_buckets[m_bucketIndex].chain.begin();
|
||||
} else {
|
||||
++m_bucketIterator;
|
||||
}
|
||||
if (!m_bucketIterator.isEnd())
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
friend class HashTable;
|
||||
explicit Iterator(HashTable& table, bool isEnd, typename SinglyLinkedList<T>::Iterator bucketIterator = SinglyLinkedList<T>::Iterator::universalEnd())
|
||||
: m_table(table)
|
||||
, m_isEnd(isEnd)
|
||||
, m_bucketIterator(bucketIterator)
|
||||
{
|
||||
if (!isEnd && !m_table.isEmpty() && !(m_bucketIterator != SinglyLinkedList<T>::Iterator::universalEnd())) {
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("bucket iterator init!\n");
|
||||
#endif
|
||||
m_bucketIterator = m_table.m_buckets[0].chain.begin();
|
||||
if (m_bucketIterator.isEnd())
|
||||
skipToNext();
|
||||
}
|
||||
}
|
||||
|
||||
HashTable& m_table;
|
||||
unsigned m_bucketIndex { 0 };
|
||||
bool m_isEnd { false };
|
||||
typename SinglyLinkedList<T>::Iterator m_bucketIterator;
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(*this, false); }
|
||||
Iterator end() { return Iterator(*this, true); }
|
||||
|
||||
class ConstIterator {
|
||||
public:
|
||||
bool operator!=(const ConstIterator& other)
|
||||
{
|
||||
if (m_isEnd && other.m_isEnd)
|
||||
return false;
|
||||
return &m_table != &other.m_table
|
||||
|| m_isEnd != other.m_isEnd
|
||||
|| m_bucketIndex != other.m_bucketIndex
|
||||
|| m_bucketIterator != other.m_bucketIterator;
|
||||
}
|
||||
const T& operator*() const
|
||||
{
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("retrieve { bucketIndex: %u, isEnd: %u }\n", m_bucketIndex, m_isEnd);
|
||||
#endif
|
||||
return *m_bucketIterator;
|
||||
}
|
||||
ConstIterator& operator++()
|
||||
{
|
||||
skipToNext();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void skipToNext()
|
||||
{
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
unsigned pass = 0;
|
||||
#endif
|
||||
while (!m_isEnd) {
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
++pass;
|
||||
printf("skipToNext pass %u, m_bucketIndex=%u\n", pass, m_bucketIndex);
|
||||
#endif
|
||||
if (m_bucketIterator.isEnd()) {
|
||||
++m_bucketIndex;
|
||||
if (m_bucketIndex >= m_table.capacity()) {
|
||||
m_isEnd = true;
|
||||
return;
|
||||
}
|
||||
const SinglyLinkedList<T>& chain = m_table.m_buckets[m_bucketIndex].chain;
|
||||
m_bucketIterator = chain.begin();
|
||||
} else {
|
||||
++m_bucketIterator;
|
||||
}
|
||||
if (!m_bucketIterator.isEnd())
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
friend class HashTable;
|
||||
ConstIterator(const HashTable& table, bool isEnd, typename SinglyLinkedList<T>::ConstIterator bucketIterator = SinglyLinkedList<T>::ConstIterator::universalEnd())
|
||||
: m_table(table)
|
||||
, m_isEnd(isEnd)
|
||||
, m_bucketIterator(bucketIterator)
|
||||
{
|
||||
if (!isEnd && !m_table.isEmpty() && !(m_bucketIterator != SinglyLinkedList<T>::ConstIterator::universalEnd())) {
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("bucket iterator init!\n");
|
||||
#endif
|
||||
const SinglyLinkedList<T>& chain = m_table.m_buckets[0].chain;
|
||||
m_bucketIterator = chain.begin();
|
||||
|
||||
skipToNext();
|
||||
}
|
||||
}
|
||||
|
||||
const HashTable& m_table;
|
||||
unsigned m_bucketIndex { 0 };
|
||||
bool m_isEnd { false };
|
||||
typename SinglyLinkedList<T>::ConstIterator m_bucketIterator;
|
||||
};
|
||||
|
||||
ConstIterator begin() const { return ConstIterator(*this, false); }
|
||||
ConstIterator end() const { return ConstIterator(*this, true); }
|
||||
|
||||
Iterator find(const T&);
|
||||
ConstIterator find(const T&) const;
|
||||
|
||||
private:
|
||||
Bucket& lookup(const T&);
|
||||
const Bucket& lookup(const T&) const;
|
||||
void rehash(unsigned capacity);
|
||||
void insert(T&&);
|
||||
|
||||
Bucket* m_buckets { nullptr };
|
||||
|
||||
unsigned m_size { 0 };
|
||||
unsigned m_capacity { 0 };
|
||||
};
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
void HashTable<T, TraitsForT>::set(T&& value)
|
||||
{
|
||||
if (!m_capacity)
|
||||
rehash(1);
|
||||
auto& bucket = lookup(value);
|
||||
for (auto& e : bucket.chain) {
|
||||
if (e == value)
|
||||
return;
|
||||
}
|
||||
if (size() >= capacity()) {
|
||||
rehash(size() + 1);
|
||||
insert(std::move(value));
|
||||
} else {
|
||||
bucket.chain.append(std::move(value));
|
||||
}
|
||||
m_size++;
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
void HashTable<T, TraitsForT>::rehash(unsigned newCapacity)
|
||||
{
|
||||
newCapacity *= 2;
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("rehash to %u buckets\n", newCapacity);
|
||||
#endif
|
||||
auto* newBuckets = new Bucket[newCapacity];
|
||||
auto* oldBuckets = m_buckets;
|
||||
unsigned oldCapacity = m_capacity;
|
||||
m_buckets = newBuckets;
|
||||
m_capacity = newCapacity;
|
||||
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("reinsert %u buckets\n", oldCapacity);
|
||||
#endif
|
||||
for (unsigned i = 0; i < oldCapacity; ++i) {
|
||||
for (auto& value : oldBuckets[i].chain) {
|
||||
insert(std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
delete [] oldBuckets;
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
void HashTable<T, TraitsForT>::clear()
|
||||
{
|
||||
delete [] m_buckets;
|
||||
m_capacity = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
void HashTable<T, TraitsForT>::insert(T&& value)
|
||||
{
|
||||
auto& bucket = lookup(value);
|
||||
bucket.chain.append(std::move(value));
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
bool HashTable<T, TraitsForT>::contains(const T& value) const
|
||||
{
|
||||
if (isEmpty())
|
||||
return false;
|
||||
auto& bucket = lookup(value);
|
||||
for (auto& e : bucket.chain) {
|
||||
if (e == value)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
auto HashTable<T, TraitsForT>::find(const T& value) -> Iterator
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
auto& bucket = lookup(value);
|
||||
auto bucketIterator = bucket.chain.find(value);
|
||||
if (bucketIterator != bucket.chain.end())
|
||||
return Iterator(*this, false, bucketIterator);
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
auto HashTable<T, TraitsForT>::find(const T& value) const -> ConstIterator
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
auto& bucket = lookup(value);
|
||||
auto bucketIterator = bucket.chain.find(value);
|
||||
if (bucketIterator != bucket.chain.end())
|
||||
return ConstIterator(*this, false, bucketIterator);
|
||||
return end();
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
typename HashTable<T, TraitsForT>::Bucket& HashTable<T, TraitsForT>::lookup(const T& value)
|
||||
{
|
||||
unsigned hash = TraitsForT::hash(value);
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("hash for ");
|
||||
TraitsForT::dump(value);
|
||||
printf(" is %u\n", hash);
|
||||
#endif
|
||||
return m_buckets[hash % m_capacity];
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
const typename HashTable<T, TraitsForT>::Bucket& HashTable<T, TraitsForT>::lookup(const T& value) const
|
||||
{
|
||||
unsigned hash = TraitsForT::hash(value);
|
||||
#ifdef HASHTABLE_DEBUG
|
||||
printf("hash for ");
|
||||
TraitsForT::dump(value);
|
||||
printf(" is %u\n", hash);
|
||||
#endif
|
||||
return m_buckets[hash % m_capacity];
|
||||
}
|
||||
|
||||
template<typename T, typename TraitsForT>
|
||||
void HashTable<T, TraitsForT>::dump() const
|
||||
{
|
||||
printf("HashTable{%p} m_size=%u, m_capacity=%u, m_buckets=%p\n", this, m_size, m_capacity, m_buckets);
|
||||
for (unsigned i = 0; i < m_capacity; ++i) {
|
||||
auto& bucket = m_buckets[i];
|
||||
printf("Bucket %u\n", i);
|
||||
for (auto& e : bucket.chain) {
|
||||
printf(" > ");
|
||||
TraitsForT::dump(e);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using AK::HashTable;
|
||||
|
18
AK/Makefile
Normal file
18
AK/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
PROGRAM = akit-test
|
||||
OBJS = StringImpl.o String.o MappedFile.o TemporaryFile.o SimpleMalloc.o kmalloc.o test.o
|
||||
|
||||
CXXFLAGS = -std=c++17 -O0 -W -Wall -ggdb3
|
||||
|
||||
all: $(PROGRAM)
|
||||
|
||||
test.o: Vector.h String.h StringImpl.h MappedFile.h HashTable.h SinglyLinkedList.h Traits.h HashMap.h TemporaryFile.h Buffer.h
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(PROGRAM)
|
||||
|
||||
$(PROGRAM): $(OBJS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
48
AK/MappedFile.cpp
Normal file
48
AK/MappedFile.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "MappedFile.h"
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdio>
|
||||
|
||||
namespace AK {
|
||||
|
||||
MappedFile::MappedFile(String&& fileName)
|
||||
: m_fileName(std::move(fileName))
|
||||
{
|
||||
m_fileLength = 1024;
|
||||
m_fd = open(m_fileName.characters(), O_RDONLY);
|
||||
|
||||
if (m_fd != -1) {
|
||||
struct stat st;
|
||||
fstat(m_fd, &st);
|
||||
m_fileLength = st.st_size;
|
||||
m_map = mmap(nullptr, m_fileLength, PROT_READ, MAP_SHARED, m_fd, 0);
|
||||
|
||||
if (m_map == MAP_FAILED)
|
||||
perror("");
|
||||
}
|
||||
|
||||
printf("MappedFile{%s} := { m_fd=%d, m_fileLength=%u, m_map=%p }\n", m_fileName.characters(), m_fd, m_fileLength, m_map);
|
||||
}
|
||||
|
||||
MappedFile::~MappedFile()
|
||||
{
|
||||
if (m_map != (void*)-1) {
|
||||
ASSERT(m_fd != -1);
|
||||
munmap(m_map, m_fileLength);
|
||||
}
|
||||
}
|
||||
|
||||
MappedFile::MappedFile(MappedFile&& other)
|
||||
: m_fileName(std::move(other.m_fileName))
|
||||
, m_fileLength(other.m_fileLength)
|
||||
, m_fd(other.m_fd)
|
||||
, m_map(other.m_map)
|
||||
{
|
||||
other.m_fileLength = 0;
|
||||
other.m_fd = -1;
|
||||
other.m_map = (void*)-1;
|
||||
}
|
||||
|
||||
}
|
||||
|
30
AK/MappedFile.h
Normal file
30
AK/MappedFile.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "String.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
class MappedFile {
|
||||
public:
|
||||
MappedFile() { }
|
||||
explicit MappedFile(String&& fileName);
|
||||
MappedFile(MappedFile&&);
|
||||
~MappedFile();
|
||||
|
||||
bool isValid() const { return m_map != (void*)-1; }
|
||||
|
||||
void* pointer() { return m_map; }
|
||||
const void* pointer() const { return m_map; }
|
||||
size_t fileLength() const { return m_fileLength; }
|
||||
|
||||
private:
|
||||
String m_fileName;
|
||||
size_t m_fileLength { 0 };
|
||||
int m_fd { -1 };
|
||||
void* m_map { (void*)-1 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::MappedFile;
|
||||
|
94
AK/OwnPtr.h
Normal file
94
AK/OwnPtr.h
Normal file
@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace AK {
|
||||
|
||||
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(std::nullptr_t) { };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
OwnPtr& operator=(std::nullptr_t)
|
||||
{
|
||||
clear();
|
||||
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(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using AK::OwnPtr;
|
||||
using AK::make;
|
||||
|
128
AK/RetainPtr.h
Normal file
128
AK/RetainPtr.h
Normal file
@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
inline void retainIfNotNull(T* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
ptr->retain();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void releaseIfNotNull(T* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
ptr->release();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class RetainPtr {
|
||||
public:
|
||||
enum AdoptTag { Adopt };
|
||||
|
||||
RetainPtr() { }
|
||||
RetainPtr(const T* ptr) : m_ptr(const_cast<T*>(ptr)) { retainIfNotNull(m_ptr); }
|
||||
RetainPtr(T* ptr) : m_ptr(ptr) { retainIfNotNull(m_ptr); }
|
||||
RetainPtr(T& object) : m_ptr(&object) { m_ptr->retain(); }
|
||||
RetainPtr(AdoptTag, T& object) : m_ptr(&object) { }
|
||||
RetainPtr(RetainPtr&& other) : m_ptr(other.leakRef()) { }
|
||||
template<typename U> RetainPtr(RetainPtr<U>&& other) : m_ptr(static_cast<T*>(other.leakRef())) { }
|
||||
~RetainPtr() { clear(); }
|
||||
RetainPtr(std::nullptr_t) { }
|
||||
|
||||
RetainPtr& operator=(RetainPtr&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
releaseIfNotNull(m_ptr);
|
||||
m_ptr = other.leakRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
RetainPtr& operator=(RetainPtr<U>&& other)
|
||||
{
|
||||
if (this != static_cast<void*>(&other)) {
|
||||
releaseIfNotNull(m_ptr);
|
||||
m_ptr = other.leakRef();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RetainPtr& operator=(T* ptr)
|
||||
{
|
||||
if (m_ptr != ptr)
|
||||
releaseIfNotNull(m_ptr);
|
||||
m_ptr = ptr;
|
||||
retainIfNotNull(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RetainPtr& operator=(T& object)
|
||||
{
|
||||
if (m_ptr != &object)
|
||||
releaseIfNotNull(m_ptr);
|
||||
m_ptr = &object;
|
||||
retainIfNotNull(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
RetainPtr& operator=(std::nullptr_t)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
RetainPtr copyRef() const
|
||||
{
|
||||
return RetainPtr(m_ptr);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
releaseIfNotNull(m_ptr);
|
||||
m_ptr = nullptr;
|
||||
}
|
||||
|
||||
bool operator!() const { return !m_ptr; }
|
||||
|
||||
typedef T* RetainPtr::*UnspecifiedBoolType;
|
||||
operator UnspecifiedBoolType() const { return m_ptr ? &RetainPtr::m_ptr : nullptr; }
|
||||
|
||||
T* leakRef()
|
||||
{
|
||||
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<typename T>
|
||||
inline RetainPtr<T> adopt(T& object)
|
||||
{
|
||||
return RetainPtr<T>(RetainPtr<T>::Adopt, object);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using AK::RetainPtr;
|
||||
using AK::adopt;
|
||||
|
43
AK/Retainable.h
Normal file
43
AK/Retainable.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
class Retainable {
|
||||
public:
|
||||
Retainable() { }
|
||||
|
||||
void retain()
|
||||
{
|
||||
ASSERT(m_retainCount);
|
||||
++m_retainCount;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
assert(m_retainCount);
|
||||
if (!--m_retainCount)
|
||||
delete static_cast<const T*>(this);
|
||||
}
|
||||
|
||||
int retainCount() const
|
||||
{
|
||||
return m_retainCount;
|
||||
}
|
||||
|
||||
protected:
|
||||
~Retainable()
|
||||
{
|
||||
ASSERT(!m_retainCount);
|
||||
}
|
||||
|
||||
private:
|
||||
int m_retainCount { 1 };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::Retainable;
|
||||
|
254
AK/SimpleMalloc.cpp
Normal file
254
AK/SimpleMalloc.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
#include "SimpleMalloc.h"
|
||||
#include "Assertions.h"
|
||||
#include "Types.h"
|
||||
#include <sys/mman.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace SimpleMalloc {
|
||||
|
||||
class AllocationBitmap {
|
||||
public:
|
||||
static AllocationBitmap wrap(byte* data, unsigned size)
|
||||
{
|
||||
return AllocationBitmap(data, size);
|
||||
}
|
||||
|
||||
~AllocationBitmap()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned size() const { return m_size; }
|
||||
bool get(unsigned index) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
return 0 != (m_data[index / 8] & (1u << (index % 8)));
|
||||
}
|
||||
void set(unsigned index, bool value) const
|
||||
{
|
||||
ASSERT(index < m_size);
|
||||
if (value)
|
||||
m_data[index / 8] |= static_cast<byte>((1u << (index % 8)));
|
||||
else
|
||||
m_data[index / 8] &= static_cast<byte>(~(1u << (index % 8)));
|
||||
}
|
||||
|
||||
private:
|
||||
AllocationBitmap(byte* data, unsigned size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
byte* m_data { nullptr };
|
||||
unsigned m_size { 0 };
|
||||
};
|
||||
|
||||
template<dword chunkSize>
|
||||
class ChunkAllocator {
|
||||
public:
|
||||
void initialize(byte* base)
|
||||
{
|
||||
m_base = base;
|
||||
m_free = capacityInAllocations();
|
||||
dump();
|
||||
}
|
||||
|
||||
static constexpr dword capacityInAllocations()
|
||||
{
|
||||
return 1048576 / chunkSize;
|
||||
}
|
||||
|
||||
static constexpr dword capacityInBytes()
|
||||
{
|
||||
return capacityInAllocations() * chunkSize;
|
||||
}
|
||||
|
||||
byte* allocate()
|
||||
{
|
||||
auto bitmap = this->bitmap();
|
||||
for (dword i = 0; i < capacityInAllocations(); ++i) {
|
||||
if (!bitmap.get(i)) {
|
||||
bitmap.set(i, true);
|
||||
--m_free;
|
||||
return pointerToChunk(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void dump() const
|
||||
{
|
||||
printf("ChunkAllocator<%u> @ %p, free: %u\n", chunkSize, m_base, m_free);
|
||||
}
|
||||
|
||||
void free(byte* ptr)
|
||||
{
|
||||
ASSERT(isInAllocator(ptr));
|
||||
auto bitmap = this->bitmap();
|
||||
auto chunkIndex = chunkIndexFromPointer(ptr);
|
||||
ASSERT(bitmap.get(chunkIndex));
|
||||
bitmap.set(chunkIndex, false);
|
||||
++m_free;
|
||||
}
|
||||
|
||||
bool isInAllocator(byte* ptr)
|
||||
{
|
||||
return ptr >= pointerToChunk(0) && ptr <= addressAfterThisAllocator();
|
||||
}
|
||||
|
||||
dword chunkIndexFromPointer(byte* ptr)
|
||||
{
|
||||
return (ptr - pointerToChunk(0)) / chunkSize;
|
||||
}
|
||||
|
||||
byte* pointerToChunk(dword index)
|
||||
{
|
||||
return m_base + sizeOfAllocationBitmapInBytes() + (index * chunkSize);
|
||||
}
|
||||
|
||||
AllocationBitmap bitmap()
|
||||
{
|
||||
return AllocationBitmap::wrap(m_base, capacityInAllocations());
|
||||
}
|
||||
|
||||
static constexpr dword sizeOfAllocationBitmapInBytes()
|
||||
{
|
||||
return capacityInAllocations() / 8;
|
||||
}
|
||||
|
||||
byte* addressAfterThisAllocator() const
|
||||
{
|
||||
return m_base + sizeOfAllocationBitmapInBytes() + capacityInBytes();
|
||||
}
|
||||
|
||||
dword numberOfFreeChunks() const
|
||||
{
|
||||
return m_free;
|
||||
}
|
||||
|
||||
private:
|
||||
byte* m_base { nullptr };
|
||||
dword m_free { capacityInAllocations() };
|
||||
};
|
||||
|
||||
struct Allocator {
|
||||
void initialize();
|
||||
void initializeIfNeeded();
|
||||
void dump();
|
||||
|
||||
ChunkAllocator<8> alloc8;
|
||||
ChunkAllocator<16> alloc16;
|
||||
ChunkAllocator<4096> alloc4096;
|
||||
ChunkAllocator<16384> alloc16384;
|
||||
|
||||
byte* space;
|
||||
bool initialized { false };
|
||||
};
|
||||
|
||||
static Allocator allocator;
|
||||
|
||||
void Allocator::initializeIfNeeded()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
initialize();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void Allocator::initialize()
|
||||
{
|
||||
space = (byte*)mmap((void*)0x20000000, 32 * MB, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT(space != MAP_FAILED);
|
||||
alloc8.initialize(space + 0x10000);
|
||||
alloc16.initialize(alloc8.addressAfterThisAllocator());
|
||||
alloc4096.initialize(alloc16.addressAfterThisAllocator());
|
||||
alloc16384.initialize(alloc4096.addressAfterThisAllocator());
|
||||
}
|
||||
|
||||
void Allocator::dump()
|
||||
{
|
||||
alloc8.dump();
|
||||
alloc16.dump();
|
||||
alloc4096.dump();
|
||||
alloc16384.dump();
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
allocator.initialize();
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
allocator.dump();
|
||||
}
|
||||
|
||||
byte* allocate(dword size)
|
||||
{
|
||||
if (!size)
|
||||
return nullptr;
|
||||
allocator.initializeIfNeeded();
|
||||
if (size <= 8) {
|
||||
if (auto* ptr = allocator.alloc8.allocate())
|
||||
return ptr;
|
||||
}
|
||||
if (size <= 16) {
|
||||
if (auto* ptr = allocator.alloc16.allocate())
|
||||
return ptr;
|
||||
}
|
||||
if (size <= 4096) {
|
||||
if (auto* ptr = allocator.alloc4096.allocate())
|
||||
return ptr;
|
||||
}
|
||||
if (size <= 16384) {
|
||||
if (auto* ptr = allocator.alloc16384.allocate())
|
||||
return ptr;
|
||||
}
|
||||
printf("SimpleMalloc: unsupported alloc size: %u\n", size);
|
||||
ASSERT_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
byte* allocateZeroed(dword size)
|
||||
{
|
||||
auto* ptr = allocate(size);
|
||||
if (!ptr)
|
||||
return nullptr;
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
byte* reallocate(byte* ptr, dword size)
|
||||
{
|
||||
// FIXME;
|
||||
ASSERT_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void free(byte* ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
allocator.initializeIfNeeded();
|
||||
if (allocator.alloc8.isInAllocator(ptr)) {
|
||||
allocator.alloc8.free(ptr);
|
||||
return;
|
||||
}
|
||||
if (allocator.alloc16.isInAllocator(ptr)) {
|
||||
allocator.alloc16.free(ptr);
|
||||
return;
|
||||
}
|
||||
if (allocator.alloc4096.isInAllocator(ptr)) {
|
||||
allocator.alloc4096.free(ptr);
|
||||
return;
|
||||
}
|
||||
if (allocator.alloc16384.isInAllocator(ptr)) {
|
||||
allocator.alloc16384.free(ptr);
|
||||
return;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
||||
|
15
AK/SimpleMalloc.h
Normal file
15
AK/SimpleMalloc.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace SimpleMalloc {
|
||||
|
||||
void initialize();
|
||||
void dump();
|
||||
byte* allocate(dword);
|
||||
byte* allocateZeroed(dword);
|
||||
void free(byte*);
|
||||
byte* reallocate(byte*, dword);
|
||||
|
||||
}
|
||||
|
114
AK/SinglyLinkedList.h
Normal file
114
AK/SinglyLinkedList.h
Normal file
@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
class SinglyLinkedList {
|
||||
private:
|
||||
struct Node {
|
||||
T value;
|
||||
Node* next { nullptr };
|
||||
};
|
||||
|
||||
public:
|
||||
SinglyLinkedList() { }
|
||||
~SinglyLinkedList() { }
|
||||
|
||||
bool isEmpty() const { return !head(); }
|
||||
|
||||
T& first() { ASSERT(head()); return head()->value; }
|
||||
const T& first() const { ASSERT(head()); return head()->value; }
|
||||
T& last() { ASSERT(head()); return tail()->value; }
|
||||
const T& last() const { ASSERT(head()); return tail()->value; }
|
||||
|
||||
void append(T&& value)
|
||||
{
|
||||
auto* node = new Node;
|
||||
node->value = std::move(value);
|
||||
if (!m_head) {
|
||||
m_head = node;
|
||||
m_tail = node;
|
||||
return;
|
||||
}
|
||||
m_tail->next = node;
|
||||
m_tail = node;
|
||||
}
|
||||
|
||||
bool containsSlow(const T& value) const
|
||||
{
|
||||
for (auto* node = m_head; node; node = node->next) {
|
||||
if (node->value == value)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
bool operator!=(const Iterator& other) { return m_node != other.m_node; }
|
||||
Iterator& operator++() { m_node = m_node->next; return *this; }
|
||||
T& operator*() { return m_node->value; }
|
||||
bool isEnd() const { return !m_node; }
|
||||
static Iterator universalEnd() { return Iterator(nullptr); }
|
||||
private:
|
||||
friend class SinglyLinkedList;
|
||||
explicit Iterator(SinglyLinkedList::Node* node) : m_node(node) { }
|
||||
SinglyLinkedList::Node* m_node;
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(m_head); }
|
||||
Iterator end() { return Iterator::universalEnd(); }
|
||||
|
||||
class ConstIterator {
|
||||
public:
|
||||
bool operator!=(const ConstIterator& other) { return m_node != other.m_node; }
|
||||
ConstIterator& operator++() { m_node = m_node->next; return *this; }
|
||||
const T& operator*() const { return m_node->value; }
|
||||
bool isEnd() const { return !m_node; }
|
||||
static ConstIterator universalEnd() { return ConstIterator(nullptr); }
|
||||
private:
|
||||
friend class SinglyLinkedList;
|
||||
explicit ConstIterator(const SinglyLinkedList::Node* node) : m_node(node) { }
|
||||
const SinglyLinkedList::Node* m_node;
|
||||
};
|
||||
|
||||
ConstIterator begin() const { return ConstIterator(m_head); }
|
||||
ConstIterator end() const { return ConstIterator::universalEnd(); }
|
||||
|
||||
ConstIterator find(const T& value) const
|
||||
{
|
||||
for (auto* node = m_head; node; node = node->next) {
|
||||
if (node->value == value)
|
||||
return ConstIterator(node);
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
Iterator find(const T& value)
|
||||
{
|
||||
for (auto* node = m_head; node; node = node->next) {
|
||||
if (node->value == value)
|
||||
return Iterator(node);
|
||||
}
|
||||
return end();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Iterator;
|
||||
|
||||
Node* head() { return m_head; }
|
||||
const Node* head() const { return m_head; }
|
||||
|
||||
Node* tail() { return m_tail; }
|
||||
const Node* tail() const { return m_tail; }
|
||||
|
||||
Node* m_head { nullptr };
|
||||
Node* m_tail { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::SinglyLinkedList;
|
||||
|
33
AK/StdLib.h
Normal file
33
AK/StdLib.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
inline T min(const T& a, const T& b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T max(const T& a, const T& b)
|
||||
{
|
||||
return a < b ? b : a;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
static inline T ceilDiv(T a, T b)
|
||||
{
|
||||
T result = a / b;
|
||||
if ((a % b) != 0)
|
||||
++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
using AK::min;
|
||||
using AK::max;
|
||||
using AK::ceilDiv;
|
||||
|
56
AK/String.cpp
Normal file
56
AK/String.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "String.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace AK {
|
||||
|
||||
bool String::operator==(const String& other) const
|
||||
{
|
||||
if (!m_impl)
|
||||
return !other.m_impl;
|
||||
|
||||
if (!other.m_impl)
|
||||
return false;
|
||||
|
||||
if (length() != other.length())
|
||||
return false;
|
||||
|
||||
return !memcmp(characters(), other.characters(), length());
|
||||
}
|
||||
|
||||
String String::empty()
|
||||
{
|
||||
return StringImpl::theEmptyStringImpl();
|
||||
}
|
||||
|
||||
Vector<String> String::split(const char separator) const
|
||||
{
|
||||
if (isEmpty())
|
||||
return { };
|
||||
|
||||
Vector<String> parts;
|
||||
|
||||
auto* characters = this->characters();
|
||||
unsigned startOfPart = 0;
|
||||
unsigned i = 0;
|
||||
for (i = 0; i < length(); ++i) {
|
||||
char ch = characters[i];
|
||||
if (ch == separator) {
|
||||
if (i != startOfPart) {
|
||||
parts.append(String(characters + startOfPart, i - startOfPart));
|
||||
}
|
||||
startOfPart = i + 1;
|
||||
}
|
||||
}
|
||||
if (startOfPart != length())
|
||||
parts.append(String(characters + startOfPart, i - startOfPart));
|
||||
return parts;
|
||||
}
|
||||
|
||||
ByteBuffer String::toByteBuffer() const
|
||||
{
|
||||
if (!m_impl)
|
||||
return nullptr;
|
||||
return ByteBuffer::copy(reinterpret_cast<const byte*>(characters()), length());
|
||||
}
|
||||
|
||||
}
|
98
AK/String.h
Normal file
98
AK/String.h
Normal file
@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "ByteBuffer.h"
|
||||
#include "RetainPtr.h"
|
||||
#include "StringImpl.h"
|
||||
#include "Traits.h"
|
||||
#include "Vector.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace AK {
|
||||
|
||||
class String {
|
||||
public:
|
||||
~String() { }
|
||||
|
||||
String() { }
|
||||
String(const String& other)
|
||||
: m_impl(const_cast<String&>(other).m_impl.copyRef())
|
||||
{
|
||||
}
|
||||
|
||||
String(String&& other)
|
||||
: m_impl(std::move(other.m_impl))
|
||||
{
|
||||
}
|
||||
|
||||
String(const char* cstring)
|
||||
: m_impl(StringImpl::create(cstring))
|
||||
{
|
||||
}
|
||||
|
||||
String(const char* cstring, size_t length)
|
||||
: m_impl(StringImpl::create(cstring, length))
|
||||
{
|
||||
}
|
||||
|
||||
String(const StringImpl& impl)
|
||||
: m_impl(const_cast<StringImpl&>(impl))
|
||||
{
|
||||
}
|
||||
|
||||
String(RetainPtr<StringImpl>&& impl)
|
||||
: m_impl(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
String toLowercase() const
|
||||
{
|
||||
if (!m_impl)
|
||||
return String();
|
||||
return m_impl->toLowercase();
|
||||
}
|
||||
|
||||
String toUppercase() const
|
||||
{
|
||||
if (!m_impl)
|
||||
return String();
|
||||
return m_impl->toUppercase();
|
||||
}
|
||||
|
||||
Vector<String> split(char separator) const;
|
||||
|
||||
bool isEmpty() const { return length() == 0; }
|
||||
unsigned length() const { return m_impl ? m_impl->length() : 0; }
|
||||
const char* characters() const { return m_impl ? m_impl->characters() : nullptr; }
|
||||
char operator[](unsigned i) const { ASSERT(m_impl); return (*m_impl)[i]; }
|
||||
|
||||
bool operator==(const String&) const;
|
||||
bool operator!=(const String& other) const { return !(*this == other); }
|
||||
|
||||
static String empty();
|
||||
|
||||
StringImpl* impl() { return m_impl.ptr(); }
|
||||
const StringImpl* impl() const { return m_impl.ptr(); }
|
||||
|
||||
String& operator=(String&& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_impl = std::move(other.m_impl);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ByteBuffer toByteBuffer() const;
|
||||
|
||||
private:
|
||||
RetainPtr<StringImpl> m_impl;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<String> {
|
||||
static unsigned hash(const String& s) { return s.impl() ? s.impl()->hash() : 0; }
|
||||
static void dump(const String& s) { printf("%s", s.characters()); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::String;
|
25
AK/StringBuilder.cpp
Normal file
25
AK/StringBuilder.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "StringBuilder.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
void StringBuilder::append(String&& str)
|
||||
{
|
||||
m_strings.append(std::move(str));
|
||||
}
|
||||
|
||||
void StringBuilder::append(char ch)
|
||||
{
|
||||
m_strings.append(StringImpl::create(&ch, 1));
|
||||
}
|
||||
|
||||
String StringBuilder::build()
|
||||
{
|
||||
auto strings = std::move(m_strings);
|
||||
if (strings.isEmpty())
|
||||
return String::empty();
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
}
|
||||
|
25
AK/StringBuilder.h
Normal file
25
AK/StringBuilder.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "String.h"
|
||||
#include "Vector.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
class StringBuilder {
|
||||
public:
|
||||
StringBuilder() { }
|
||||
~StringBuilder() { }
|
||||
|
||||
void append(String&&);
|
||||
void append(char);
|
||||
|
||||
String build();
|
||||
|
||||
private:
|
||||
Vector<String> m_strings;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::StringBuilder;
|
||||
|
149
AK/StringImpl.cpp
Normal file
149
AK/StringImpl.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "StringImpl.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include "kmalloc.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
StringImpl& StringImpl::theEmptyStringImpl()
|
||||
{
|
||||
static StringImpl* s = new StringImpl(ConstructTheEmptyStringImpl);
|
||||
return *s;
|
||||
}
|
||||
|
||||
StringImpl::~StringImpl()
|
||||
{
|
||||
}
|
||||
|
||||
static inline size_t allocationSizeForStringImpl(unsigned length)
|
||||
{
|
||||
return sizeof(StringImpl) + (sizeof(char) * length) + sizeof(char);
|
||||
}
|
||||
|
||||
RetainPtr<StringImpl> StringImpl::createUninitialized(unsigned length, char*& buffer)
|
||||
{
|
||||
if (!length)
|
||||
return theEmptyStringImpl();
|
||||
|
||||
void* slot = kmalloc(allocationSizeForStringImpl(length));
|
||||
if (!slot)
|
||||
return nullptr;
|
||||
|
||||
auto newStringImpl = adopt(*new (slot) StringImpl(ConstructWithInlineBuffer, length));
|
||||
buffer = const_cast<char*>(newStringImpl->m_characters);
|
||||
buffer[length] = '\0';
|
||||
return newStringImpl;
|
||||
}
|
||||
|
||||
RetainPtr<StringImpl> StringImpl::create(const char* cstring, size_t length)
|
||||
{
|
||||
if (!cstring)
|
||||
return nullptr;
|
||||
|
||||
if (!*cstring)
|
||||
return theEmptyStringImpl();
|
||||
|
||||
char* buffer;
|
||||
auto newStringImpl = createUninitialized(length, buffer);
|
||||
memcpy(buffer, cstring, length * sizeof(char));
|
||||
|
||||
return newStringImpl;
|
||||
}
|
||||
|
||||
RetainPtr<StringImpl> StringImpl::create(const char* cstring)
|
||||
{
|
||||
if (!cstring)
|
||||
return nullptr;
|
||||
|
||||
return create(cstring, strlen(cstring));
|
||||
}
|
||||
|
||||
static inline bool isASCIILowercase(char c)
|
||||
{
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
static inline bool isASCIIUppercase(char c)
|
||||
{
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
static inline char toASCIILowercase(char c)
|
||||
{
|
||||
if (isASCIIUppercase(c))
|
||||
return c | 0x20;
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline char toASCIIUppercase(char c)
|
||||
{
|
||||
if (isASCIILowercase(c))
|
||||
return c & ~0x20;
|
||||
return c;
|
||||
}
|
||||
|
||||
RetainPtr<StringImpl> StringImpl::toLowercase() const
|
||||
{
|
||||
if (!m_length)
|
||||
return const_cast<StringImpl*>(this);
|
||||
|
||||
for (unsigned i = 0; i < m_length; ++i) {
|
||||
if (!isASCIILowercase(m_characters[i]))
|
||||
goto slowPath;
|
||||
}
|
||||
return const_cast<StringImpl*>(this);
|
||||
|
||||
slowPath:
|
||||
char* buffer;
|
||||
auto lowercased = createUninitialized(m_length, buffer);
|
||||
for (unsigned i = 0; i < m_length; ++i) {
|
||||
buffer[i] = toASCIILowercase(m_characters[i]);
|
||||
}
|
||||
|
||||
return lowercased;
|
||||
}
|
||||
|
||||
RetainPtr<StringImpl> StringImpl::toUppercase() const
|
||||
{
|
||||
if (!m_length)
|
||||
return const_cast<StringImpl*>(this);
|
||||
|
||||
for (unsigned i = 0; i < m_length; ++i) {
|
||||
if (!isASCIIUppercase(m_characters[i]))
|
||||
goto slowPath;
|
||||
}
|
||||
return const_cast<StringImpl*>(this);
|
||||
|
||||
slowPath:
|
||||
char* buffer;
|
||||
auto uppercased = createUninitialized(m_length, buffer);
|
||||
for (unsigned i = 0; i < m_length; ++i) {
|
||||
buffer[i] = toASCIIUppercase(m_characters[i]);
|
||||
}
|
||||
|
||||
return uppercased;
|
||||
}
|
||||
|
||||
void StringImpl::computeHash() const
|
||||
{
|
||||
if (!length()) {
|
||||
m_hash = 0;
|
||||
} else {
|
||||
unsigned hash = 0;
|
||||
for (unsigned i = 0; i < m_length; ++i) {
|
||||
hash += m_characters[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
hash += hash << 3;
|
||||
hash ^= hash >> 11;
|
||||
hash += hash << 15;
|
||||
m_hash = hash;
|
||||
}
|
||||
m_hasHash = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
50
AK/StringImpl.h
Normal file
50
AK/StringImpl.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Retainable.h"
|
||||
#include "RetainPtr.h"
|
||||
|
||||
namespace AK {
|
||||
|
||||
class StringImpl : public Retainable<StringImpl> {
|
||||
public:
|
||||
static RetainPtr<StringImpl> createUninitialized(unsigned length, char*& buffer);
|
||||
static RetainPtr<StringImpl> create(const char* cstring);
|
||||
static RetainPtr<StringImpl> create(const char* cstring, size_t length);
|
||||
RetainPtr<StringImpl> toLowercase() const;
|
||||
RetainPtr<StringImpl> toUppercase() const;
|
||||
|
||||
static StringImpl& theEmptyStringImpl();
|
||||
|
||||
~StringImpl();
|
||||
|
||||
unsigned length() const { return m_length; }
|
||||
const char* characters() const { return m_characters; }
|
||||
char operator[](unsigned i) const { ASSERT(i < m_length); return m_characters[i]; }
|
||||
|
||||
unsigned hash() const
|
||||
{
|
||||
if (!m_hasHash)
|
||||
computeHash();
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ConstructTheEmptyStringImplTag { ConstructTheEmptyStringImpl };
|
||||
explicit StringImpl(ConstructTheEmptyStringImplTag) : m_characters("") { }
|
||||
|
||||
enum ConstructWithInlineBufferTag { ConstructWithInlineBuffer };
|
||||
explicit StringImpl(ConstructWithInlineBufferTag, unsigned length) : m_length(length), m_characters(m_inlineBuffer) { }
|
||||
|
||||
void computeHash() const;
|
||||
|
||||
unsigned m_length { 0 };
|
||||
bool m_ownsBuffer { true };
|
||||
mutable bool m_hasHash { false };
|
||||
const char* m_characters { nullptr };
|
||||
mutable unsigned m_hash { 0 };
|
||||
char m_inlineBuffer[0];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::StringImpl;
|
33
AK/TemporaryFile.cpp
Normal file
33
AK/TemporaryFile.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "TemporaryFile.h"
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
TemporaryFile::TemporaryFile()
|
||||
{
|
||||
char nameBuffer[] = "/tmp/AKTemporaryFile.XXXXXX";
|
||||
int fd = mkstemp(nameBuffer);
|
||||
if (fd != -1) {
|
||||
m_stream = fdopen(fd, "w+");
|
||||
m_fileName = nameBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryFile::~TemporaryFile()
|
||||
{
|
||||
if (isValid()) {
|
||||
unlink(m_fileName.characters());
|
||||
fclose(m_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void TemporaryFile::sync()
|
||||
{
|
||||
if (m_stream)
|
||||
fflush(m_stream);
|
||||
}
|
||||
|
||||
}
|
||||
|
26
AK/TemporaryFile.h
Normal file
26
AK/TemporaryFile.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "String.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile();
|
||||
~TemporaryFile();
|
||||
|
||||
bool isValid() const { return m_stream; }
|
||||
FILE* stream() { return m_stream; }
|
||||
String fileName() const { return m_fileName; }
|
||||
void sync();
|
||||
|
||||
private:
|
||||
FILE* m_stream { nullptr };
|
||||
String m_fileName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::TemporaryFile;
|
||||
|
25
AK/Traits.h
Normal file
25
AK/Traits.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T>
|
||||
struct Traits
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<int> {
|
||||
static unsigned hash(int i) { return i; }
|
||||
static void dump(int i) { printf("%d", i); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<unsigned> {
|
||||
static unsigned hash(unsigned u) { return u; }
|
||||
static void dump(unsigned u) { printf("%u", u); }
|
||||
};
|
||||
|
||||
}
|
||||
|
17
AK/Types.h
Normal file
17
AK/Types.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef uint16_t word;
|
||||
typedef uint32_t dword;
|
||||
typedef uint64_t qword;
|
||||
|
||||
typedef int8_t signed_byte;
|
||||
typedef int16_t signed_word;
|
||||
typedef int32_t signed_dword;
|
||||
typedef int64_t signed_qword;
|
||||
|
||||
constexpr unsigned KB = 1024;
|
||||
constexpr unsigned MB = KB * KB;
|
||||
constexpr unsigned GB = KB * KB * KB;
|
169
AK/Vector.h
Normal file
169
AK/Vector.h
Normal file
@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include "Assertions.h"
|
||||
#include "OwnPtr.h"
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<typename T> class Vector;
|
||||
|
||||
template<typename T>
|
||||
class VectorImpl {
|
||||
public:
|
||||
~VectorImpl() { }
|
||||
static OwnPtr<VectorImpl> create(unsigned capacity)
|
||||
{
|
||||
size_t size = sizeof(VectorImpl) + sizeof(T) * capacity;
|
||||
void* slot = kmalloc(size);
|
||||
return OwnPtr<VectorImpl>(new (slot) VectorImpl(capacity));
|
||||
}
|
||||
|
||||
unsigned size() const { return m_size; }
|
||||
unsigned capacity() const { return m_capacity; }
|
||||
|
||||
T& at(unsigned i) { return *slot(i); }
|
||||
const T& at(unsigned i) const { return *slot(i); }
|
||||
|
||||
private:
|
||||
friend class Vector<T>;
|
||||
|
||||
VectorImpl(unsigned capacity) : m_capacity(capacity) { }
|
||||
|
||||
T* tail() { return reinterpret_cast<T*>(this + 1); }
|
||||
T* slot(unsigned i) { return &tail()[i]; }
|
||||
|
||||
const T* tail() const { return reinterpret_cast<const T*>(this + 1); }
|
||||
const T* slot(unsigned i) const { return &tail()[i]; }
|
||||
|
||||
unsigned m_size { 0 };
|
||||
unsigned m_capacity;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Vector {
|
||||
public:
|
||||
Vector() { }
|
||||
~Vector() { clear(); }
|
||||
|
||||
Vector(Vector&& other)
|
||||
: m_impl(std::move(other.m_impl))
|
||||
{
|
||||
}
|
||||
|
||||
Vector& operator=(Vector&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
m_impl = std::move(other.m_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
at(i).~T();
|
||||
}
|
||||
m_impl = nullptr;
|
||||
}
|
||||
|
||||
bool isEmpty() const { return size() == 0; }
|
||||
unsigned size() const { return m_impl ? m_impl->size() : 0; }
|
||||
unsigned capacity() const { return m_impl ? m_impl->capacity() : 0; }
|
||||
|
||||
const T& at(unsigned i) const { return m_impl->at(i); }
|
||||
T& at(unsigned i) { return m_impl->at(i); }
|
||||
|
||||
const T& operator[](unsigned i) const { return at(i); }
|
||||
T& operator[](unsigned i) { return at(i); }
|
||||
|
||||
const T& first() const { return at(0); }
|
||||
T& first() { return at(0); }
|
||||
|
||||
const T& last() const { return at(size() - 1); }
|
||||
T& last() { return at(size() - 1); }
|
||||
|
||||
T takeLast()
|
||||
{
|
||||
ASSERT(!isEmpty());
|
||||
T value = std::move(last());
|
||||
last().~T();
|
||||
--m_impl->m_size;
|
||||
return value;
|
||||
}
|
||||
|
||||
void append(T&& value)
|
||||
{
|
||||
ensureCapacity(size() + 1);
|
||||
new (m_impl->slot(m_impl->m_size)) T(std::move(value));
|
||||
++m_impl->m_size;
|
||||
}
|
||||
|
||||
void append(const T& value)
|
||||
{
|
||||
ensureCapacity(size() + 1);
|
||||
new (m_impl->slot(m_impl->m_size)) T(value);
|
||||
++m_impl->m_size;
|
||||
}
|
||||
|
||||
void ensureCapacity(unsigned neededCapacity)
|
||||
{
|
||||
if (capacity() >= neededCapacity)
|
||||
return;
|
||||
size_t newCapacity = paddedCapacity(neededCapacity);
|
||||
auto newImpl = VectorImpl<T>::create(newCapacity);
|
||||
if (m_impl) {
|
||||
newImpl->m_size = m_impl->m_size;
|
||||
for (unsigned i = 0; i < size(); ++i) {
|
||||
new (newImpl->slot(i)) T(std::move(m_impl->at(i)));
|
||||
m_impl->at(i).~T();
|
||||
}
|
||||
}
|
||||
m_impl = std::move(newImpl);
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
bool operator!=(const Iterator& other) { return m_index != other.m_index; }
|
||||
Iterator& operator++() { ++m_index; return *this; }
|
||||
T& operator*() { return m_vector[m_index]; }
|
||||
private:
|
||||
friend class Vector;
|
||||
Iterator(Vector& vector, unsigned index) : m_vector(vector), m_index(index) { }
|
||||
Vector& m_vector;
|
||||
unsigned m_index { 0 };
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(*this, 0); }
|
||||
Iterator end() { return Iterator(*this, size()); }
|
||||
|
||||
class ConstIterator {
|
||||
public:
|
||||
bool operator!=(const ConstIterator& other) { return m_index != other.m_index; }
|
||||
ConstIterator& operator++() { ++m_index; return *this; }
|
||||
const T& operator*() const { return m_vector[m_index]; }
|
||||
private:
|
||||
friend class Vector;
|
||||
ConstIterator(const Vector& vector, const unsigned index) : m_vector(vector), m_index(index) { }
|
||||
const Vector& m_vector;
|
||||
unsigned m_index { 0 };
|
||||
};
|
||||
|
||||
ConstIterator begin() const { return Iterator(*this, 0); }
|
||||
ConstIterator end() const { return Iterator(*this, size()); }
|
||||
|
||||
private:
|
||||
static unsigned paddedCapacity(unsigned capacity)
|
||||
{
|
||||
return std::max(4u, capacity + (capacity / 4) + 4);
|
||||
}
|
||||
|
||||
OwnPtr<VectorImpl<T>> m_impl;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using AK::Vector;
|
||||
|
66
AK/kmalloc.cpp
Normal file
66
AK/kmalloc.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
#include <cstdio>
|
||||
#include "SimpleMalloc.h"
|
||||
#include <new>
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* kcalloc(dword nmemb, dword size)
|
||||
{
|
||||
if (!nmemb || !size)
|
||||
return nullptr;
|
||||
return SimpleMalloc::allocateZeroed(nmemb * size);
|
||||
}
|
||||
|
||||
void* kmalloc(dword size)
|
||||
{
|
||||
if (!size)
|
||||
return nullptr;
|
||||
return SimpleMalloc::allocate(size);
|
||||
}
|
||||
|
||||
void kfree(void* ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
SimpleMalloc::free((byte*)ptr);
|
||||
}
|
||||
|
||||
void* krealloc(void* ptr, dword size)
|
||||
{
|
||||
if (!ptr)
|
||||
return ptr;
|
||||
return SimpleMalloc::reallocate((byte*)ptr, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void* operator new(std::size_t size)
|
||||
{
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
void* operator new[](std::size_t size)
|
||||
{
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete(void* ptr, size_t)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void* ptr, size_t)
|
||||
{
|
||||
return kfree(ptr);
|
||||
}
|
||||
|
13
AK/kmalloc.h
Normal file
13
AK/kmalloc.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* kcalloc(dword nmemb, dword size);
|
||||
void* kmalloc(dword size);
|
||||
void kfree(void* ptr);
|
||||
void* krealloc(void* ptr, dword size);
|
||||
|
||||
}
|
||||
|
131
AK/test.cpp
Normal file
131
AK/test.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include "String.h"
|
||||
//#include "StringBuilder.h"
|
||||
#include "Vector.h"
|
||||
#include <stdio.h>
|
||||
#include "HashTable.h"
|
||||
#include "SinglyLinkedList.h"
|
||||
#include "HashMap.h"
|
||||
#include "TemporaryFile.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
String empty = "";
|
||||
|
||||
char* buffer;
|
||||
auto test = StringImpl::createUninitialized(3, buffer);
|
||||
auto hello = String("hello");
|
||||
auto Hello = String("Hello");
|
||||
|
||||
printf("hello: '%s'\n", hello.characters());
|
||||
printf("Hello: '%s'\n", Hello.characters());
|
||||
printf("'Hello'.lower(): '%s'\n", Hello.toLowercase().characters());
|
||||
printf("'hello'.upper(): '%s'\n", Hello.toUppercase().characters());
|
||||
|
||||
Vector<String> strings;
|
||||
strings.append("a");
|
||||
strings.append("b");
|
||||
strings.append("c");
|
||||
strings.append("d");
|
||||
strings.append("e");
|
||||
strings.append("f");
|
||||
strings.append("g");
|
||||
|
||||
auto g = strings.takeLast();
|
||||
|
||||
for (unsigned i = 0; i < strings.size(); ++i) {
|
||||
printf("[%u]: '%s'\n", i, strings[i].characters());
|
||||
}
|
||||
|
||||
printf("snodde sista: '%s'\n", g.characters());
|
||||
printf("kvar:\n");
|
||||
|
||||
for (auto& s : strings) {
|
||||
printf(" > %s\n", s.characters());
|
||||
}
|
||||
|
||||
#if 0
|
||||
StringBuilder builder;
|
||||
builder.append("HEJ");
|
||||
builder.append(' ');
|
||||
builder.append("KAJ");
|
||||
|
||||
printf("byggd: '%s'\n", builder.build().characters());
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
HashTable<int> ints;
|
||||
ints.set(10);
|
||||
ints.set(20);
|
||||
ints.set(30);
|
||||
ints.dump();
|
||||
|
||||
ASSERT(ints.size() == 3);
|
||||
ASSERT(ints.contains(10));
|
||||
ASSERT(ints.contains(20));
|
||||
ASSERT(ints.contains(30));
|
||||
ASSERT(!ints.contains(0));
|
||||
ASSERT(!ints.contains(40));
|
||||
|
||||
HashTable<String> sss;
|
||||
sss.set("foo");
|
||||
sss.set("bar");
|
||||
sss.set("baz");
|
||||
sss.set("bee");
|
||||
ASSERT(sss.size() == 4);
|
||||
sss.dump();
|
||||
ASSERT(sss.contains("foo"));
|
||||
ASSERT(sss.contains("bar"));
|
||||
ASSERT(sss.contains("baz"));
|
||||
ASSERT(sss.contains("bee"));
|
||||
ASSERT(!sss.contains("boo"));
|
||||
ASSERT(!sss.contains(""));
|
||||
ASSERT(!sss.contains(String()));
|
||||
|
||||
printf(">>> iterate Hash:\n");
|
||||
for (auto& s : sss) {
|
||||
printf("+ %s\n", s.characters());
|
||||
}
|
||||
printf("<<<\n");
|
||||
#endif
|
||||
|
||||
SinglyLinkedList<int> list;
|
||||
list.append(3);
|
||||
list.append(6);
|
||||
list.append(9);
|
||||
ASSERT(!list.isEmpty());
|
||||
ASSERT(list.first() == 3);
|
||||
ASSERT(list.last() == 9);
|
||||
|
||||
for (int i : list) {
|
||||
printf("Iterated to %d\n", i);
|
||||
}
|
||||
|
||||
HashMap<String, int> map;
|
||||
map.set("lol", 100);
|
||||
map.set("kek", 500);
|
||||
map.set("zoo", 300);
|
||||
ASSERT(map.size() == 3);
|
||||
map.dump();
|
||||
for (auto& it : map) {
|
||||
printf("[%s] := %d\n", it.key.characters(), it.value);
|
||||
}
|
||||
|
||||
auto found = map.find("kek");
|
||||
if (found != map.end()) {
|
||||
printf("found 'kek', key: %s, value: %d\n", (*found).key.characters(), (*found).value);
|
||||
} else {
|
||||
printf("not found\n");
|
||||
}
|
||||
|
||||
auto charbuf = Buffer<char>::createUninitialized(1024);
|
||||
printf("charbuf.size() = %u\n", charbuf->size());
|
||||
|
||||
{
|
||||
Vector<String> problem;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
problem.append("test");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
3
ELFLoader/.gitignore
vendored
Normal file
3
ELFLoader/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
run
|
||||
*.o
|
||||
AK
|
188
ELFLoader/ELFImage.cpp
Normal file
188
ELFLoader/ELFImage.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
#include "ELFImage.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
ELFImage::ELFImage(MappedFile&& file)
|
||||
: m_file(std::move(file))
|
||||
{
|
||||
m_isValid = parse();
|
||||
}
|
||||
|
||||
ELFImage::~ELFImage()
|
||||
{
|
||||
}
|
||||
|
||||
static const char* objectFileTypeToString(Elf32_Half type)
|
||||
{
|
||||
switch (type) {
|
||||
case ET_NONE: return "None";
|
||||
case ET_REL: return "Relocatable";
|
||||
case ET_EXEC: return "Executable";
|
||||
case ET_DYN: return "Shared object";
|
||||
case ET_CORE: return "Core";
|
||||
default: return "(?)";
|
||||
}
|
||||
}
|
||||
|
||||
const char* ELFImage::sectionIndexToString(unsigned index)
|
||||
{
|
||||
if (index == SHN_UNDEF)
|
||||
return "Undefined";
|
||||
if (index >= SHN_LORESERVE)
|
||||
return "Reserved";
|
||||
return section(index).name();
|
||||
}
|
||||
|
||||
unsigned ELFImage::symbolCount() const
|
||||
{
|
||||
return section(m_symbolTableSectionIndex).entryCount();
|
||||
}
|
||||
|
||||
void ELFImage::dump()
|
||||
{
|
||||
printf("AK::ELFImage{%p} {\n", this);
|
||||
printf(" isValid: %u\n", isValid());
|
||||
|
||||
if (!isValid()) {
|
||||
printf("}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf(" type: %s\n", objectFileTypeToString(header().e_type));
|
||||
printf(" machine: %u\n", header().e_machine);
|
||||
printf(" entry: %08x\n", header().e_entry);
|
||||
printf(" shoff: %u\n", header().e_shoff);
|
||||
printf(" shnum: %u\n", header().e_shnum);
|
||||
printf(" shstrndx: %u\n", header().e_shstrndx);
|
||||
|
||||
for (unsigned i = 0; i < header().e_shnum; ++i) {
|
||||
auto& section = this->section(i);
|
||||
printf(" Section %u: {\n", i);
|
||||
printf(" name: %s\n", section.name());
|
||||
printf(" type: %x\n", section.type());
|
||||
printf(" offset: %x\n", section.offset());
|
||||
printf(" size: %u\n", section.size());
|
||||
printf(" \n");
|
||||
printf(" }\n");
|
||||
}
|
||||
|
||||
printf("Symbol count: %u (table is %u)\n", symbolCount(), m_symbolTableSectionIndex);
|
||||
for (unsigned i = 1; i < symbolCount(); ++i) {
|
||||
auto& sym = symbol(i);
|
||||
printf("Symbol @%u:\n", i);
|
||||
printf(" Name: %s\n", sym.name());
|
||||
printf(" In section: %s\n", sectionIndexToString(sym.sectionIndex()));
|
||||
printf(" Value: %08x\n", sym.value());
|
||||
printf(" Size: %u\n", sym.size());
|
||||
}
|
||||
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
unsigned ELFImage::sectionCount() const
|
||||
{
|
||||
return header().e_shnum;
|
||||
}
|
||||
|
||||
bool ELFImage::parse()
|
||||
{
|
||||
// We only support i386.
|
||||
if (header().e_machine != 3)
|
||||
return false;
|
||||
|
||||
// First locate the string tables.
|
||||
for (unsigned i = 0; i < sectionCount(); ++i) {
|
||||
auto& sh = sectionHeader(i);
|
||||
if (sh.sh_type == SHT_SYMTAB) {
|
||||
ASSERT(!m_symbolTableSectionIndex);
|
||||
m_symbolTableSectionIndex = i;
|
||||
}
|
||||
if (sh.sh_type == SHT_STRTAB && i != header().e_shstrndx) {
|
||||
ASSERT(!m_stringTableSectionIndex);
|
||||
m_stringTableSectionIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Then create a name-to-index map.
|
||||
for (unsigned i = 0; i < sectionCount(); ++i) {
|
||||
auto& section = this->section(i);
|
||||
m_sections.set(section.name(), std::move(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ELFImage::sectionHeaderTableString(unsigned offset) const
|
||||
{
|
||||
auto& sh = sectionHeader(header().e_shstrndx);
|
||||
if (sh.sh_type != SHT_STRTAB)
|
||||
return nullptr;
|
||||
return rawData(sh.sh_offset + offset);
|
||||
}
|
||||
|
||||
const char* ELFImage::tableString(unsigned offset) const
|
||||
{
|
||||
auto& sh = sectionHeader(m_stringTableSectionIndex);
|
||||
if (sh.sh_type != SHT_STRTAB)
|
||||
return nullptr;
|
||||
return rawData(sh.sh_offset + offset);
|
||||
}
|
||||
|
||||
const char* ELFImage::rawData(unsigned offset) const
|
||||
{
|
||||
return reinterpret_cast<const char*>(m_file.pointer()) + offset;
|
||||
}
|
||||
|
||||
const Elf32_Ehdr& ELFImage::header() const
|
||||
{
|
||||
return *reinterpret_cast<const Elf32_Ehdr*>(rawData(0));
|
||||
}
|
||||
|
||||
const Elf32_Shdr& ELFImage::sectionHeader(unsigned index) const
|
||||
{
|
||||
ASSERT(index < header().e_shnum);
|
||||
return *reinterpret_cast<const Elf32_Shdr*>(rawData(header().e_shoff + (index * sizeof(Elf32_Shdr))));
|
||||
}
|
||||
|
||||
const ELFImage::Symbol ELFImage::symbol(unsigned index) const
|
||||
{
|
||||
ASSERT(index < symbolCount());
|
||||
auto* rawSyms = reinterpret_cast<const Elf32_Sym*>(rawData(section(m_symbolTableSectionIndex).offset()));
|
||||
return Symbol(*this, index, rawSyms[index]);
|
||||
}
|
||||
|
||||
const ELFImage::Section ELFImage::section(unsigned index) const
|
||||
{
|
||||
ASSERT(index < sectionCount());
|
||||
return Section(*this, index);
|
||||
}
|
||||
|
||||
const ELFImage::Relocation ELFImage::RelocationSection::relocation(unsigned index) const
|
||||
{
|
||||
ASSERT(index < relocationCount());
|
||||
auto* rels = reinterpret_cast<const Elf32_Rel*>(m_image.rawData(offset()));
|
||||
return Relocation(m_image, rels[index]);
|
||||
}
|
||||
|
||||
const ELFImage::RelocationSection ELFImage::Section::relocations() const
|
||||
{
|
||||
// FIXME: This is ugly.
|
||||
char relocationSectionName[128];
|
||||
sprintf(relocationSectionName, ".rel%s", name());
|
||||
|
||||
printf("looking for '%s'\n", relocationSectionName);
|
||||
auto relocationSection = m_image.lookupSection(relocationSectionName);
|
||||
if (relocationSection.type() != SHT_REL)
|
||||
return static_cast<const RelocationSection>(m_image.section(0));
|
||||
|
||||
printf("Found relocations for %s in %s\n", name(), relocationSection.name());
|
||||
return static_cast<const RelocationSection>(relocationSection);
|
||||
}
|
||||
|
||||
const ELFImage::Section ELFImage::lookupSection(const char* name) const
|
||||
{
|
||||
if (auto it = m_sections.find(name); it != m_sections.end())
|
||||
return section((*it).value);
|
||||
return section(0);
|
||||
}
|
||||
|
156
ELFLoader/ELFImage.h
Normal file
156
ELFLoader/ELFImage.h
Normal file
@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/MappedFile.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include "elf.h"
|
||||
|
||||
class ELFImage {
|
||||
public:
|
||||
explicit ELFImage(MappedFile&&);
|
||||
~ELFImage();
|
||||
void dump();
|
||||
bool isValid() const { return m_isValid; }
|
||||
bool parse();
|
||||
|
||||
class Section;
|
||||
class RelocationSection;
|
||||
class Symbol;
|
||||
class Relocation;
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
Symbol(const ELFImage& image, unsigned index, const Elf32_Sym& sym)
|
||||
: m_image(image)
|
||||
, m_sym(sym)
|
||||
, m_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
~Symbol() { }
|
||||
|
||||
const char* name() const { return m_image.tableString(m_sym.st_name); }
|
||||
unsigned sectionIndex() const { return m_sym.st_shndx; }
|
||||
unsigned value() const { return m_sym.st_value; }
|
||||
unsigned size() const { return m_sym.st_size; }
|
||||
unsigned index() const { return m_index; }
|
||||
unsigned type() const { return ELF32_ST_TYPE(m_sym.st_info); }
|
||||
const Section section() const { return m_image.section(sectionIndex()); }
|
||||
|
||||
private:
|
||||
const ELFImage& m_image;
|
||||
const Elf32_Sym& m_sym;
|
||||
const unsigned m_index;
|
||||
};
|
||||
|
||||
class Section {
|
||||
public:
|
||||
Section(const ELFImage& image, unsigned sectionIndex)
|
||||
: m_image(image)
|
||||
, m_sectionHeader(image.sectionHeader(sectionIndex))
|
||||
, m_sectionIndex(sectionIndex)
|
||||
{
|
||||
}
|
||||
~Section() { }
|
||||
|
||||
const char* name() const { return m_image.sectionHeaderTableString(m_sectionHeader.sh_name); }
|
||||
unsigned type() const { return m_sectionHeader.sh_type; }
|
||||
unsigned offset() const { return m_sectionHeader.sh_offset; }
|
||||
unsigned size() const { return m_sectionHeader.sh_size; }
|
||||
unsigned entrySize() const { return m_sectionHeader.sh_entsize; }
|
||||
unsigned entryCount() const { return size() / entrySize(); }
|
||||
const char* rawData() const { return m_image.rawData(m_sectionHeader.sh_offset); }
|
||||
bool isUndefined() const { return m_sectionIndex == SHN_UNDEF; }
|
||||
const RelocationSection relocations() const;
|
||||
|
||||
protected:
|
||||
friend class RelocationSection;
|
||||
const ELFImage& m_image;
|
||||
const Elf32_Shdr& m_sectionHeader;
|
||||
unsigned m_sectionIndex;
|
||||
};
|
||||
|
||||
class RelocationSection : public Section {
|
||||
public:
|
||||
RelocationSection(const Section& section)
|
||||
: Section(section.m_image, section.m_sectionIndex)
|
||||
{
|
||||
}
|
||||
unsigned relocationCount() const { return entryCount(); }
|
||||
const Relocation relocation(unsigned index) const;
|
||||
template<typename F> void forEachRelocation(F) const;
|
||||
};
|
||||
|
||||
class Relocation {
|
||||
public:
|
||||
Relocation(const ELFImage& image, const Elf32_Rel& rel)
|
||||
: m_image(image)
|
||||
, m_rel(rel)
|
||||
{
|
||||
}
|
||||
|
||||
~Relocation() { }
|
||||
|
||||
unsigned offset() const { return m_rel.r_offset; }
|
||||
unsigned type() const { return ELF32_R_TYPE(m_rel.r_info); }
|
||||
unsigned symbolIndex() const { return ELF32_R_SYM(m_rel.r_info); }
|
||||
const Symbol symbol() const { return m_image.symbol(symbolIndex()); }
|
||||
|
||||
private:
|
||||
const ELFImage& m_image;
|
||||
const Elf32_Rel& m_rel;
|
||||
};
|
||||
|
||||
unsigned symbolCount() const;
|
||||
unsigned sectionCount() const;
|
||||
|
||||
const Symbol symbol(unsigned) const;
|
||||
const Section section(unsigned) const;
|
||||
|
||||
template<typename F> void forEachSectionOfType(unsigned, F) const;
|
||||
template<typename F> void forEachSymbol(F) const;
|
||||
|
||||
// NOTE: Returns section(0) if section with name is not found.
|
||||
// FIXME: I don't love this API.
|
||||
const Section lookupSection(const char* name) const;
|
||||
|
||||
private:
|
||||
bool parseHeader();
|
||||
const char* rawData(unsigned offset) const;
|
||||
const Elf32_Ehdr& header() const;
|
||||
const Elf32_Shdr& sectionHeader(unsigned) const;
|
||||
const char* tableString(unsigned offset) const;
|
||||
const char* sectionHeaderTableString(unsigned offset) const;
|
||||
const char* sectionIndexToString(unsigned index);
|
||||
|
||||
MappedFile m_file;
|
||||
HashMap<String, unsigned> m_sections;
|
||||
bool m_isValid { false };
|
||||
unsigned m_symbolTableSectionIndex { 0 };
|
||||
unsigned m_stringTableSectionIndex { 0 };
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
inline void ELFImage::forEachSectionOfType(unsigned type, F func) const
|
||||
{
|
||||
for (unsigned i = 0; i < sectionCount(); ++i) {
|
||||
auto& section = this->section(i);
|
||||
if (section.type() == type)
|
||||
func(section);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void ELFImage::RelocationSection::forEachRelocation(F func) const
|
||||
{
|
||||
for (unsigned i = 0; i < relocationCount(); ++i)
|
||||
func(relocation(i));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline void ELFImage::forEachSymbol(F func) const
|
||||
{
|
||||
for (unsigned i = 0; i < symbolCount(); ++i)
|
||||
func(symbol(i));
|
||||
}
|
||||
|
115
ELFLoader/ELFLoader.cpp
Normal file
115
ELFLoader/ELFLoader.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "ELFLoader.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
ELFLoader::ELFLoader(ExecSpace& execSpace, MappedFile&& file)
|
||||
: m_execSpace(execSpace)
|
||||
{
|
||||
m_image = make<ELFImage>(std::move(file));
|
||||
}
|
||||
|
||||
ELFLoader::~ELFLoader()
|
||||
{
|
||||
}
|
||||
|
||||
bool ELFLoader::load()
|
||||
{
|
||||
m_image->dump();
|
||||
if (!m_image->isValid())
|
||||
return false;
|
||||
|
||||
layout();
|
||||
exportSymbols();
|
||||
performRelocations();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ELFLoader::layout()
|
||||
{
|
||||
printf("[ELFLoader] Layout\n");
|
||||
m_image->forEachSectionOfType(SHT_PROGBITS, [this] (const ELFImage::Section& section) {
|
||||
printf("[ELFLoader] Allocating progbits section: %s\n", section.name());
|
||||
char* ptr = m_execSpace.allocateArea(section.name(), section.size());
|
||||
memcpy(ptr, section.rawData(), section.size());
|
||||
m_sections.set(section.name(), std::move(ptr));
|
||||
});
|
||||
}
|
||||
|
||||
void* ELFLoader::lookup(const ELFImage::Symbol& symbol)
|
||||
{
|
||||
if (symbol.section().isUndefined())
|
||||
return m_execSpace.symbolPtr(symbol.name());
|
||||
return areaForSection(symbol.section()) + symbol.value();
|
||||
}
|
||||
|
||||
char* ELFLoader::areaForSection(const ELFImage::Section& section)
|
||||
{
|
||||
return areaForSectionName(section.name());
|
||||
}
|
||||
|
||||
char* ELFLoader::areaForSectionName(const char* name)
|
||||
{
|
||||
if (auto it = m_sections.find(name); it != m_sections.end())
|
||||
return (*it).value;
|
||||
ASSERT_NOT_REACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ELFLoader::performRelocations()
|
||||
{
|
||||
printf("[ELFLoader] Performing relocations\n");
|
||||
|
||||
m_image->forEachSectionOfType(SHT_PROGBITS, [this] (const ELFImage::Section& section) {
|
||||
auto& relocations = section.relocations();
|
||||
if (relocations.isUndefined())
|
||||
return;
|
||||
relocations.forEachRelocation([this, section] (const ELFImage::Relocation& relocation) {
|
||||
auto symbol = relocation.symbol();
|
||||
auto& patchPtr = *reinterpret_cast<ptrdiff_t*>(areaForSection(section) + relocation.offset());
|
||||
|
||||
switch (relocation.type()) {
|
||||
case R_386_PC32: {
|
||||
char* targetPtr = (char*)lookup(symbol);
|
||||
ptrdiff_t relativeOffset = (char*)targetPtr - ((char*)&patchPtr + 4);
|
||||
printf("[ELFLoader] Relocate PC32: offset=%08x, symbol=%u(%s) value=%08x target=%p, offset=%d\n",
|
||||
relocation.offset(),
|
||||
symbol.index(),
|
||||
symbol.name(),
|
||||
symbol.value(),
|
||||
targetPtr,
|
||||
relativeOffset
|
||||
);
|
||||
patchPtr = relativeOffset;
|
||||
break;
|
||||
}
|
||||
case R_386_32: {
|
||||
printf("[ELFLoader] Relocate Abs32: symbol=%u(%s), value=%08x, section=%s\n",
|
||||
symbol.index(),
|
||||
symbol.name(),
|
||||
symbol.value(),
|
||||
symbol.section().name()
|
||||
);
|
||||
char* targetPtr = areaForSection(symbol.section()) + symbol.value();
|
||||
patchPtr += (ptrdiff_t)targetPtr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ELFLoader::exportSymbols()
|
||||
{
|
||||
m_image->forEachSymbol([&] (const ELFImage::Symbol symbol) {
|
||||
printf("symbol: %u, type=%u, name=%s\n", symbol.index(), symbol.type(), symbol.name());
|
||||
if (symbol.type() == STT_FUNC)
|
||||
m_execSpace.addSymbol(symbol.name(), areaForSectionName(".text") + symbol.value(), symbol.size());
|
||||
// FIXME: What about other symbol types?
|
||||
});
|
||||
}
|
||||
|
29
ELFLoader/ELFLoader.h
Normal file
29
ELFLoader/ELFLoader.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/MappedFile.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include "ExecSpace.h"
|
||||
#include "ELFImage.h"
|
||||
|
||||
class ELFLoader {
|
||||
public:
|
||||
ELFLoader(ExecSpace&, MappedFile&&);
|
||||
~ELFLoader();
|
||||
|
||||
bool load();
|
||||
|
||||
private:
|
||||
void layout();
|
||||
void performRelocations();
|
||||
void exportSymbols();
|
||||
void* lookup(const ELFImage::Symbol&);
|
||||
char* areaForSection(const ELFImage::Section&);
|
||||
char* areaForSectionName(const char*);
|
||||
|
||||
ExecSpace& m_execSpace;
|
||||
HashMap<String, char*> m_sections;
|
||||
OwnPtr<ELFImage> m_image;
|
||||
};
|
||||
|
80
ELFLoader/ExecSpace.cpp
Normal file
80
ELFLoader/ExecSpace.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "ExecSpace.h"
|
||||
#include "ELFLoader.h"
|
||||
#include <AK/TemporaryFile.h>
|
||||
#include <unistd.h>
|
||||
|
||||
ExecSpace::ExecSpace()
|
||||
{
|
||||
initializeBuiltins();
|
||||
}
|
||||
|
||||
ExecSpace::~ExecSpace()
|
||||
{
|
||||
}
|
||||
|
||||
void ExecSpace::initializeBuiltins()
|
||||
{
|
||||
m_symbols.set("puts", { (char*)puts, 0 });
|
||||
}
|
||||
|
||||
bool ExecSpace::loadELF(MappedFile&& file)
|
||||
{
|
||||
ELFLoader loader(*this, std::move(file));
|
||||
if (!loader.load())
|
||||
return false;
|
||||
printf("[ExecSpace] ELF loaded, symbol map now:\n");
|
||||
for (auto& s : m_symbols) {
|
||||
printf("> %p: %s (%u)\n",
|
||||
s.value.ptr,
|
||||
s.key.characters(),
|
||||
s.value.size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void disassemble(const char* data, size_t length)
|
||||
{
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
TemporaryFile temp;
|
||||
if (!temp.isValid()) {
|
||||
fprintf(stderr, "Unable to create temp file for disassembly.\n");
|
||||
return;
|
||||
}
|
||||
fprintf(temp.stream(), "db ");
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
fprintf(temp.stream(), "0x%02x, ", (unsigned char)data[i]);
|
||||
}
|
||||
fprintf(temp.stream(), "\n");
|
||||
temp.sync();
|
||||
|
||||
char cmdbuf[128];
|
||||
sprintf(cmdbuf, "nasm -f bin -o /dev/stdout %s | ndisasm -b32 -", temp.fileName().characters());
|
||||
system(cmdbuf);
|
||||
}
|
||||
|
||||
char* ExecSpace::symbolPtr(const char* name)
|
||||
{
|
||||
if (auto it = m_symbols.find(name); it != m_symbols.end()) {
|
||||
printf("[ELFLoader] symbolPtr(%s) dump:\n", name);
|
||||
auto& symbol = (*it).value;
|
||||
disassemble(symbol.ptr, symbol.size);
|
||||
return symbol.ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
char* ExecSpace::allocateArea(String&& name, unsigned size)
|
||||
{
|
||||
char* ptr = static_cast<char*>(malloc(size));
|
||||
ASSERT(ptr);
|
||||
m_areas.append(make<Area>(std::move(name), ptr, size));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void ExecSpace::addSymbol(String&& name, char* ptr, unsigned size)
|
||||
{
|
||||
m_symbols.set(std::move(name), { ptr, size });
|
||||
}
|
53
ELFLoader/ExecSpace.h
Normal file
53
ELFLoader/ExecSpace.h
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/MappedFile.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
class ELFLoader;
|
||||
|
||||
class ExecSpace {
|
||||
public:
|
||||
struct Area {
|
||||
Area(String&& n, char* m, unsigned s)
|
||||
: name(std::move(n))
|
||||
, memory(m)
|
||||
, size(s)
|
||||
{
|
||||
}
|
||||
|
||||
String name;
|
||||
char* memory { 0 };
|
||||
unsigned size { 0 };
|
||||
};
|
||||
|
||||
struct PtrAndSize {
|
||||
PtrAndSize() { }
|
||||
PtrAndSize(char* p, unsigned s)
|
||||
: ptr(p)
|
||||
, size(s)
|
||||
{
|
||||
}
|
||||
|
||||
char* ptr { nullptr };
|
||||
unsigned size { 0 };
|
||||
};
|
||||
|
||||
ExecSpace();
|
||||
~ExecSpace();
|
||||
|
||||
bool loadELF(MappedFile&&);
|
||||
|
||||
char* symbolPtr(const char* name);
|
||||
|
||||
char* allocateArea(String&& name, unsigned size);
|
||||
void addSymbol(String&& name, char* ptr, unsigned size);
|
||||
|
||||
private:
|
||||
void initializeBuiltins();
|
||||
|
||||
Vector<OwnPtr<Area>> m_areas;
|
||||
HashMap<String, PtrAndSize> m_symbols;
|
||||
};
|
||||
|
38
ELFLoader/Makefile
Normal file
38
ELFLoader/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
PROGRAM = run
|
||||
|
||||
AK_OBJS = \
|
||||
../AK/String.o \
|
||||
../AK/StringImpl.o \
|
||||
../AK/MappedFile.o \
|
||||
../AK/TemporaryFile.o \
|
||||
../AK/kmalloc.o
|
||||
|
||||
CSILLA_OBJS = \
|
||||
ExecSpace.o \
|
||||
ELFImage.o \
|
||||
ELFLoader.o \
|
||||
test.o
|
||||
|
||||
TEST_OBJ = \
|
||||
_test.o
|
||||
|
||||
TEST_OBJ_CXXFLAGS = -Os -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables
|
||||
|
||||
OBJS = $(AK_OBJS) $(CSILLA_OBJS)
|
||||
|
||||
CXXFLAGS = -std=c++17 -Os -W -Wall -I. -I.. -ggdb3
|
||||
|
||||
all: $(PROGRAM) $(TEST_OBJ)
|
||||
|
||||
_test.o: _test.cpp
|
||||
$(CXX) $(TEST_OBJ_CXXFLAGS) -c $<
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TEST_OBJ) $(PROGRAM)
|
||||
|
||||
$(PROGRAM): $(OBJS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
29
ELFLoader/_test.cpp
Normal file
29
ELFLoader/_test.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" const char hello_string[] = "Hello World!";
|
||||
|
||||
extern "C" int foo()
|
||||
{
|
||||
volatile int i = 3;
|
||||
i = 4;
|
||||
return i;
|
||||
}
|
||||
|
||||
extern "C" int bar()
|
||||
{
|
||||
return foo();
|
||||
}
|
||||
|
||||
extern "C" int baz()
|
||||
{
|
||||
return bar();
|
||||
}
|
||||
|
||||
extern "C" int EntryPoint()
|
||||
{
|
||||
puts(hello_string);
|
||||
printf("abc!\n");
|
||||
printf("def!\n");
|
||||
return 10 + baz();
|
||||
}
|
||||
|
2645
ELFLoader/elf.h
Normal file
2645
ELFLoader/elf.h
Normal file
File diff suppressed because it is too large
Load Diff
24
ELFLoader/test.cpp
Normal file
24
ELFLoader/test.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "ExecSpace.h"
|
||||
#include <cstdio>
|
||||
|
||||
typedef int (*MainFunctionPtr)(void);
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
MappedFile f("_test.o");
|
||||
if (!f.isValid()) {
|
||||
fprintf(stderr, "Failed to map file :(\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ExecSpace space;
|
||||
space.loadELF(std::move(f));
|
||||
|
||||
auto func = reinterpret_cast<MainFunctionPtr>(space.symbolPtr("EntryPoint"));
|
||||
printf("func: %p\n", func);
|
||||
|
||||
int z = func();
|
||||
printf("func() returned %d\n", z);
|
||||
|
||||
return 0;
|
||||
}
|
4
VirtualFileSystem/.gitignore
vendored
Normal file
4
VirtualFileSystem/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
test
|
||||
*.o
|
||||
*.swp
|
||||
AK
|
9
VirtualFileSystem/BlockDevice.cpp
Normal file
9
VirtualFileSystem/BlockDevice.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "BlockDevice.h"
|
||||
|
||||
BlockDevice::BlockDevice()
|
||||
{
|
||||
}
|
||||
|
||||
BlockDevice::~BlockDevice()
|
||||
{
|
||||
}
|
20
VirtualFileSystem/BlockDevice.h
Normal file
20
VirtualFileSystem/BlockDevice.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/Retainable.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
class BlockDevice : public Retainable<BlockDevice> {
|
||||
public:
|
||||
virtual ~BlockDevice();
|
||||
|
||||
virtual unsigned blockSize() const = 0;
|
||||
virtual bool readBlock(unsigned index, byte*) const = 0;
|
||||
virtual bool writeBlock(unsigned index, const byte*) = 0;
|
||||
virtual const char* className() const = 0;
|
||||
virtual bool read(qword offset, unsigned length, byte*) const = 0;
|
||||
virtual bool write(qword offset, unsigned length, const byte*) = 0;
|
||||
|
||||
protected:
|
||||
BlockDevice();
|
||||
};
|
||||
|
78
VirtualFileSystem/DeviceBackedFileSystem.cpp
Normal file
78
VirtualFileSystem/DeviceBackedFileSystem.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "DeviceBackedFileSystem.h"
|
||||
|
||||
//#define DBFS_DEBUG
|
||||
|
||||
DeviceBackedFileSystem::DeviceBackedFileSystem(RetainPtr<BlockDevice>&& device)
|
||||
: m_device(std::move(device))
|
||||
{
|
||||
ASSERT(m_device);
|
||||
}
|
||||
|
||||
DeviceBackedFileSystem::~DeviceBackedFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
bool DeviceBackedFileSystem::writeBlock(unsigned index, const ByteBuffer& data)
|
||||
{
|
||||
ASSERT(data.size() == blockSize());
|
||||
#ifdef DBFS_DEBUG
|
||||
printf("DeviceBackedFileSystem::writeBlock %u\n", index);
|
||||
#endif
|
||||
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
|
||||
return device().write(baseOffset, blockSize(), data.pointer());
|
||||
}
|
||||
|
||||
bool DeviceBackedFileSystem::writeBlocks(unsigned index, unsigned count, const ByteBuffer& data)
|
||||
{
|
||||
#ifdef DBFS_DEBUG
|
||||
printf("DeviceBackedFileSystem::writeBlocks %u x%u\n", index, count);
|
||||
#endif
|
||||
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
|
||||
return device().write(baseOffset, count * blockSize(), data.pointer());
|
||||
}
|
||||
|
||||
ByteBuffer DeviceBackedFileSystem::readBlock(unsigned index) const
|
||||
{
|
||||
#ifdef DBFS_DEBUG
|
||||
printf("DeviceBackedFileSystem::readBlock %u\n", index);
|
||||
#endif
|
||||
auto buffer = ByteBuffer::createUninitialized(blockSize());
|
||||
qword baseOffset = static_cast<qword>(index) * static_cast<qword>(blockSize());
|
||||
auto* bufferPointer = buffer.pointer();
|
||||
device().read(baseOffset, blockSize(), bufferPointer);
|
||||
ASSERT(buffer.size() == blockSize());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ByteBuffer DeviceBackedFileSystem::readBlocks(unsigned index, unsigned count) const
|
||||
{
|
||||
if (!count)
|
||||
return nullptr;
|
||||
if (count == 1)
|
||||
return readBlock(index);
|
||||
auto blocks = ByteBuffer::createUninitialized(count * blockSize());
|
||||
byte* out = blocks.pointer();
|
||||
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
auto block = readBlock(index + i);
|
||||
if (!block)
|
||||
return nullptr;
|
||||
memcpy(out, block.pointer(), block.size());
|
||||
out += blockSize();
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
void DeviceBackedFileSystem::setBlockSize(unsigned blockSize)
|
||||
{
|
||||
if (blockSize == m_blockSize)
|
||||
return;
|
||||
m_blockSize = blockSize;
|
||||
invalidateCaches();
|
||||
}
|
||||
|
||||
void DeviceBackedFileSystem::invalidateCaches()
|
||||
{
|
||||
// FIXME: Implement block cache.
|
||||
}
|
30
VirtualFileSystem/DeviceBackedFileSystem.h
Normal file
30
VirtualFileSystem/DeviceBackedFileSystem.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
class DeviceBackedFileSystem : public FileSystem {
|
||||
public:
|
||||
virtual ~DeviceBackedFileSystem() override;
|
||||
|
||||
BlockDevice& device() { return *m_device; }
|
||||
const BlockDevice& device() const { return *m_device; }
|
||||
|
||||
unsigned blockSize() const { return m_blockSize; }
|
||||
|
||||
protected:
|
||||
explicit DeviceBackedFileSystem(RetainPtr<BlockDevice>&&);
|
||||
|
||||
void setBlockSize(unsigned);
|
||||
void invalidateCaches();
|
||||
|
||||
ByteBuffer readBlock(unsigned index) const;
|
||||
ByteBuffer readBlocks(unsigned index, unsigned count) const;
|
||||
|
||||
bool writeBlock(unsigned index, const ByteBuffer&);
|
||||
bool writeBlocks(unsigned index, unsigned count, const ByteBuffer&);
|
||||
|
||||
private:
|
||||
unsigned m_blockSize { 0 };
|
||||
RetainPtr<BlockDevice> m_device;
|
||||
};
|
746
VirtualFileSystem/Ext2FileSystem.cpp
Normal file
746
VirtualFileSystem/Ext2FileSystem.cpp
Normal file
@ -0,0 +1,746 @@
|
||||
#include "Ext2FileSystem.h"
|
||||
#include "ext2_fs.h"
|
||||
#include <AK/Bitmap.h>
|
||||
#include <AK/StdLib.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "kmalloc.h"
|
||||
|
||||
//#define EXT2_DEBUG
|
||||
|
||||
RetainPtr<Ext2FileSystem> Ext2FileSystem::create(RetainPtr<BlockDevice> device)
|
||||
{
|
||||
return adopt(*new Ext2FileSystem(std::move(device)));
|
||||
}
|
||||
|
||||
Ext2FileSystem::Ext2FileSystem(RetainPtr<BlockDevice> device)
|
||||
: DeviceBackedFileSystem(std::move(device))
|
||||
{
|
||||
}
|
||||
|
||||
Ext2FileSystem::~Ext2FileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
ByteBuffer Ext2FileSystem::readSuperBlock() const
|
||||
{
|
||||
auto buffer = ByteBuffer::createUninitialized(1024);
|
||||
device().readBlock(2, buffer.pointer());
|
||||
device().readBlock(3, buffer.offsetPointer(512));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::writeSuperBlock(const ext2_super_block& sb)
|
||||
{
|
||||
const byte* raw = (const byte*)&sb;
|
||||
bool success;
|
||||
success = device().writeBlock(2, raw);
|
||||
ASSERT(success);
|
||||
success = device().writeBlock(3, raw + 512);
|
||||
ASSERT(success);
|
||||
// FIXME: This is an ugly way to refresh the superblock cache. :-|
|
||||
superBlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::firstBlockOfGroup(unsigned groupIndex) const
|
||||
{
|
||||
return superBlock().s_first_data_block + (groupIndex * superBlock().s_blocks_per_group);
|
||||
}
|
||||
|
||||
const ext2_super_block& Ext2FileSystem::superBlock() const
|
||||
{
|
||||
if (!m_cachedSuperBlock)
|
||||
m_cachedSuperBlock = readSuperBlock();
|
||||
return *reinterpret_cast<ext2_super_block*>(m_cachedSuperBlock.pointer());
|
||||
}
|
||||
|
||||
const ext2_group_desc& Ext2FileSystem::blockGroupDescriptor(unsigned groupIndex) const
|
||||
{
|
||||
// FIXME: Should this fail gracefully somehow?
|
||||
ASSERT(groupIndex <= m_blockGroupCount);
|
||||
|
||||
if (!m_cachedBlockGroupDescriptorTable) {
|
||||
unsigned blocksToRead = ceilDiv(m_blockGroupCount * sizeof(ext2_group_desc), blockSize());
|
||||
printf("[ext2fs] block group count: %u, blocks-to-read: %u\n", m_blockGroupCount, blocksToRead);
|
||||
unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1;
|
||||
printf("[ext2fs] first block of BGDT: %u\n", firstBlockOfBGDT);
|
||||
m_cachedBlockGroupDescriptorTable = readBlocks(firstBlockOfBGDT, blocksToRead);
|
||||
}
|
||||
return reinterpret_cast<ext2_group_desc*>(m_cachedBlockGroupDescriptorTable.pointer())[groupIndex - 1];
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::initialize()
|
||||
{
|
||||
auto& superBlock = this->superBlock();
|
||||
printf("[ext2fs] super block magic: %04x (super block size: %u)\n", superBlock.s_magic, sizeof(ext2_super_block));
|
||||
if (superBlock.s_magic != EXT2_SUPER_MAGIC)
|
||||
return false;
|
||||
|
||||
printf("[ext2fs] %u inodes, %u blocks\n", superBlock.s_inodes_count, superBlock.s_blocks_count);
|
||||
printf("[ext2fs] block size = %u\n", EXT2_BLOCK_SIZE(&superBlock));
|
||||
printf("[ext2fs] first data block = %u\n", superBlock.s_first_data_block);
|
||||
printf("[ext2fs] inodes per block = %u\n", inodesPerBlock());
|
||||
printf("[ext2fs] inodes per group = %u\n", inodesPerGroup());
|
||||
printf("[ext2fs] free inodes = %u\n", superBlock.s_free_inodes_count);
|
||||
|
||||
printf("[ext2fs] desc per block = %u\n", EXT2_DESC_PER_BLOCK(&superBlock));
|
||||
printf("[ext2fs] desc size = %u\n", EXT2_DESC_SIZE(&superBlock));
|
||||
|
||||
setBlockSize(EXT2_BLOCK_SIZE(&superBlock));
|
||||
|
||||
m_blockGroupCount = ceilDiv(superBlock.s_blocks_count, superBlock.s_blocks_per_group);
|
||||
|
||||
if (m_blockGroupCount == 0) {
|
||||
printf("[ext2fs] no block groups :(\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 1; i <= m_blockGroupCount; ++i) {
|
||||
auto& group = blockGroupDescriptor(i);
|
||||
printf("[ext2fs] group[%u] { block_bitmap: %u, inode_bitmap: %u, inode_table: %u }\n",
|
||||
i,
|
||||
group.bg_block_bitmap,
|
||||
group.bg_inode_bitmap,
|
||||
group.bg_inode_table);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Ext2FileSystem::className() const
|
||||
{
|
||||
return "ext2fs";
|
||||
}
|
||||
|
||||
InodeIdentifier Ext2FileSystem::rootInode() const
|
||||
{
|
||||
return { id(), EXT2_ROOT_INO };
|
||||
}
|
||||
|
||||
#ifdef EXT2_DEBUG
|
||||
static void dumpExt2Inode(const ext2_inode& inode)
|
||||
{
|
||||
printf("Dump of ext2_inode:\n");
|
||||
printf(" i_size: %u\n", inode.i_size);
|
||||
printf(" i_mode: %u\n", inode.i_mode);
|
||||
printf(" i_blocks: %u\n", inode.i_blocks);
|
||||
printf(" i_uid: %u\n", inode.i_uid);
|
||||
printf(" i_gid: %u\n", inode.i_gid);
|
||||
}
|
||||
#endif
|
||||
|
||||
ByteBuffer Ext2FileSystem::readBlockContainingInode(unsigned inode, unsigned& blockIndex, unsigned& offset) const
|
||||
{
|
||||
auto& superBlock = this->superBlock();
|
||||
|
||||
if (inode != EXT2_ROOT_INO && inode < EXT2_FIRST_INO(&superBlock))
|
||||
return { };
|
||||
|
||||
if (inode > superBlock.s_inodes_count)
|
||||
return { };
|
||||
|
||||
auto& bgd = blockGroupDescriptor(groupIndexFromInode(inode));
|
||||
|
||||
offset = ((inode - 1) % inodesPerGroup()) * inodeSize();
|
||||
blockIndex = bgd.bg_inode_table + (offset >> EXT2_BLOCK_SIZE_BITS(&superBlock));
|
||||
offset &= blockSize() - 1;
|
||||
|
||||
return readBlock(blockIndex);
|
||||
}
|
||||
|
||||
OwnPtr<ext2_inode> Ext2FileSystem::lookupExt2Inode(unsigned inode) const
|
||||
{
|
||||
unsigned blockIndex;
|
||||
unsigned offset;
|
||||
auto block = readBlockContainingInode(inode, blockIndex, offset);
|
||||
|
||||
if (!block)
|
||||
return nullptr;
|
||||
|
||||
auto* e2inode = reinterpret_cast<ext2_inode*>(kmalloc(inodeSize()));
|
||||
memcpy(e2inode, reinterpret_cast<ext2_inode*>(block.offsetPointer(offset)), inodeSize());
|
||||
#ifdef EXT2_DEBUG
|
||||
dumpExt2Inode(*e2inode);
|
||||
#endif
|
||||
|
||||
return OwnPtr<ext2_inode>(e2inode);
|
||||
}
|
||||
|
||||
InodeMetadata Ext2FileSystem::inodeMetadata(InodeIdentifier inode) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
|
||||
auto e2inode = lookupExt2Inode(inode.index());
|
||||
if (!e2inode)
|
||||
return InodeMetadata();
|
||||
|
||||
InodeMetadata metadata;
|
||||
metadata.inode = inode;
|
||||
metadata.size = e2inode->i_size;
|
||||
metadata.mode = e2inode->i_mode;
|
||||
metadata.uid = e2inode->i_uid;
|
||||
metadata.gid = e2inode->i_gid;
|
||||
metadata.linkCount = e2inode->i_links_count;
|
||||
metadata.atime = e2inode->i_atime;
|
||||
metadata.ctime = e2inode->i_ctime;
|
||||
metadata.mtime = e2inode->i_mtime;
|
||||
metadata.dtime = e2inode->i_dtime;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
Vector<unsigned> Ext2FileSystem::blockListForInode(const ext2_inode& e2inode) const
|
||||
{
|
||||
unsigned entriesPerBlock = EXT2_ADDR_PER_BLOCK(&superBlock());
|
||||
|
||||
// NOTE: i_blocks is number of 512-byte blocks, not number of fs-blocks.
|
||||
unsigned blockCount = e2inode.i_blocks / (blockSize() / 512);
|
||||
unsigned blocksRemaining = blockCount;
|
||||
Vector<unsigned> list;
|
||||
list.ensureCapacity(blocksRemaining);
|
||||
|
||||
unsigned directCount = min(blockCount, (unsigned)EXT2_NDIR_BLOCKS);
|
||||
for (unsigned i = 0; i < directCount; ++i) {
|
||||
list.append(e2inode.i_block[i]);
|
||||
--blocksRemaining;
|
||||
}
|
||||
|
||||
if (!blocksRemaining)
|
||||
return list;
|
||||
|
||||
auto processBlockArray = [&] (unsigned arrayBlockIndex, std::function<void(unsigned)> callback) {
|
||||
auto arrayBlock = readBlock(arrayBlockIndex);
|
||||
ASSERT(arrayBlock);
|
||||
auto* array = reinterpret_cast<const __u32*>(arrayBlock.pointer());
|
||||
unsigned count = min(blocksRemaining, entriesPerBlock);
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
if (!array[i]) {
|
||||
blocksRemaining = 0;
|
||||
return;
|
||||
}
|
||||
callback(array[i]);
|
||||
--blocksRemaining;
|
||||
}
|
||||
};
|
||||
|
||||
processBlockArray(e2inode.i_block[EXT2_IND_BLOCK], [&] (unsigned entry) {
|
||||
list.append(entry);
|
||||
});
|
||||
|
||||
if (!blocksRemaining)
|
||||
return list;
|
||||
|
||||
processBlockArray(e2inode.i_block[EXT2_DIND_BLOCK], [&] (unsigned entry) {
|
||||
processBlockArray(entry, [&] (unsigned entry) {
|
||||
list.append(entry);
|
||||
});
|
||||
});
|
||||
|
||||
if (!blocksRemaining)
|
||||
return list;
|
||||
|
||||
processBlockArray(e2inode.i_block[EXT2_TIND_BLOCK], [&] (unsigned entry) {
|
||||
processBlockArray(entry, [&] (unsigned entry) {
|
||||
processBlockArray(entry, [&] (unsigned entry) {
|
||||
list.append(entry);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
ByteBuffer Ext2FileSystem::readInode(InodeIdentifier inode) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
|
||||
auto e2inode = lookupExt2Inode(inode.index());
|
||||
if (!e2inode) {
|
||||
printf("[ext2fs] readInode: metadata lookup for inode %u failed\n", inode.index());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Symbolic links shorter than 60 characters are store inline inside the i_block array.
|
||||
// This avoids wasting an entire block on short links. (Most links are short.)
|
||||
static const unsigned maxInlineSymlinkLength = 60;
|
||||
if (isSymbolicLink(e2inode->i_mode) && e2inode->i_size < maxInlineSymlinkLength) {
|
||||
auto inlineSymlink = ByteBuffer::createUninitialized(e2inode->i_size);
|
||||
memcpy(inlineSymlink.pointer(), e2inode->i_block, e2inode->i_size);
|
||||
return inlineSymlink;
|
||||
}
|
||||
|
||||
auto list = blockListForInode(*e2inode);
|
||||
if (list.isEmpty()) {
|
||||
printf("[ext2fs] readInode: empty block list for inode %u\n", inode.index());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto contents = ByteBuffer::createUninitialized(list.size() * blockSize());
|
||||
auto* out = contents.pointer();
|
||||
|
||||
for (unsigned i = 0; i < list.size(); ++i) {
|
||||
auto block = readBlock(list[i]);
|
||||
if (!block) {
|
||||
printf("[ext2fs] readInode: readBlock(%u) failed\n", list[i]);
|
||||
return nullptr;
|
||||
}
|
||||
memcpy(out, block.pointer(), block.size());
|
||||
out += blockSize();
|
||||
}
|
||||
|
||||
#ifdef EXT2_DEBUG
|
||||
printf("trim from %u to %u\n", contents.size(), e2inode->i_size);
|
||||
#endif
|
||||
contents.trim(e2inode->i_size);
|
||||
return contents;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::writeInode(InodeIdentifier inode, const ByteBuffer& data)
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
|
||||
auto e2inode = lookupExt2Inode(inode.index());
|
||||
if (!e2inode) {
|
||||
printf("[ext2fs] writeInode: metadata lookup for inode %u failed\n", inode.index());
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Support writing to symlink inodes.
|
||||
ASSERT(!isSymbolicLink(e2inode->i_mode));
|
||||
|
||||
unsigned blocksNeededBefore = ceilDiv(e2inode->i_size, blockSize());
|
||||
unsigned blocksNeededAfter = ceilDiv(data.size(), blockSize());
|
||||
|
||||
// FIXME: Support growing or shrinking the block list.
|
||||
ASSERT(blocksNeededBefore == blocksNeededAfter);
|
||||
|
||||
auto list = blockListForInode(*e2inode);
|
||||
if (list.isEmpty()) {
|
||||
printf("[ext2fs] writeInode: empty block list for inode %u\n", inode.index());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < list.size(); ++i) {
|
||||
auto section = data.slice(i * blockSize(), blockSize());
|
||||
printf("section = %p (%u)\n", section.pointer(), section.size());
|
||||
bool success = writeBlock(list[i], section);
|
||||
ASSERT(success);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::enumerateDirectoryInode(InodeIdentifier inode, std::function<bool(const DirectoryEntry&)> callback) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
ASSERT(isDirectoryInode(inode.index()));
|
||||
|
||||
#ifdef EXT2_DEBUG
|
||||
printf("[ext2fs] Enumerating directory contents of inode %u:\n", inode.index());
|
||||
#endif
|
||||
|
||||
auto buffer = readInode(inode);
|
||||
ASSERT(buffer);
|
||||
auto* entry = reinterpret_cast<ext2_dir_entry_2*>(buffer.pointer());
|
||||
|
||||
char namebuf[EXT2_NAME_LEN + 1];
|
||||
|
||||
while (entry < buffer.endPointer()) {
|
||||
if (entry->inode != 0) {
|
||||
memcpy(namebuf, entry->name, entry->name_len);
|
||||
namebuf[entry->name_len] = 0;
|
||||
#ifdef EXT2_DEBUG
|
||||
printf("inode: %u, name_len: %u, rec_len: %u, name: %s\n", entry->inode, entry->name_len, entry->rec_len, namebuf);
|
||||
#endif
|
||||
if (!callback({ namebuf, { id(), entry->inode }, entry->file_type }))
|
||||
break;
|
||||
}
|
||||
entry = (ext2_dir_entry_2*)((char*)entry + entry->rec_len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name)
|
||||
{
|
||||
auto e2inodeForDirectory = lookupExt2Inode(directoryInode);
|
||||
ASSERT(e2inodeForDirectory);
|
||||
ASSERT(isDirectory(e2inodeForDirectory->i_mode));
|
||||
|
||||
//#ifdef EXT2_DEBUG
|
||||
printf("[ext2fs] Adding inode %u with name '%s' to directory %u\n", inode, name.characters(), directoryInode);
|
||||
//#endif
|
||||
|
||||
Vector<DirectoryEntry> entries;
|
||||
bool nameAlreadyExists = false;
|
||||
enumerateDirectoryInode({ id(), directoryInode }, [&] (const DirectoryEntry& entry) {
|
||||
if (entry.name == name) {
|
||||
nameAlreadyExists = true;
|
||||
return false;
|
||||
}
|
||||
entries.append(entry);
|
||||
return true;
|
||||
});
|
||||
if (nameAlreadyExists) {
|
||||
printf("[ext2fs] Name '%s' already exists in directory inode %u\n", name.characters(), directoryInode);
|
||||
return false;
|
||||
}
|
||||
|
||||
entries.append({ name, { id(), inode } });
|
||||
|
||||
return writeDirectoryInode(directoryInode, std::move(entries));
|
||||
}
|
||||
|
||||
class BufferStream {
|
||||
public:
|
||||
explicit BufferStream(ByteBuffer& buffer)
|
||||
: m_buffer(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
void operator<<(byte value)
|
||||
{
|
||||
m_buffer[m_offset++] = value & 0xffu;
|
||||
}
|
||||
|
||||
void operator<<(word value)
|
||||
{
|
||||
m_buffer[m_offset++] = value & 0xffu;
|
||||
m_buffer[m_offset++] = (byte)(value >> 8) & 0xffu;
|
||||
}
|
||||
|
||||
void operator<<(dword value)
|
||||
{
|
||||
m_buffer[m_offset++] = value & 0xffu;
|
||||
m_buffer[m_offset++] = (byte)(value >> 8) & 0xffu;
|
||||
m_buffer[m_offset++] = (byte)(value >> 16) & 0xffu;
|
||||
m_buffer[m_offset++] = (byte)(value >> 24) & 0xffu;
|
||||
}
|
||||
|
||||
void operator<<(const String& value)
|
||||
{
|
||||
for (unsigned i = 0; i < value.length(); ++i)
|
||||
m_buffer[m_offset++] = value[i];
|
||||
}
|
||||
|
||||
void fillToEnd(byte ch)
|
||||
{
|
||||
while (m_offset < m_buffer.size())
|
||||
m_buffer[m_offset++] = ch;
|
||||
}
|
||||
|
||||
private:
|
||||
ByteBuffer& m_buffer;
|
||||
size_t m_offset { 0 };
|
||||
};
|
||||
|
||||
bool Ext2FileSystem::writeDirectoryInode(unsigned directoryInode, Vector<DirectoryEntry>&& entries)
|
||||
{
|
||||
printf("[ext2fs] New directory inode %u contents to write:\n", directoryInode);
|
||||
|
||||
unsigned directorySize = 0;
|
||||
for (auto& entry : entries) {
|
||||
printf(" - %08u %s\n", entry.inode.index(), entry.name.characters());
|
||||
directorySize += EXT2_DIR_REC_LEN(entry.name.length());
|
||||
}
|
||||
|
||||
unsigned blocksNeeded = ceilDiv(directorySize, blockSize());
|
||||
unsigned occupiedSize = blocksNeeded * blockSize();
|
||||
|
||||
printf("[ext2fs] directory size: %u (occupied: %u)\n", directorySize, occupiedSize);
|
||||
|
||||
auto directoryData = ByteBuffer::createUninitialized(occupiedSize);
|
||||
|
||||
BufferStream stream(directoryData);
|
||||
for (unsigned i = 0; i < entries.size(); ++i) {
|
||||
auto& entry = entries[i];
|
||||
|
||||
unsigned recordLength = EXT2_DIR_REC_LEN(entry.name.length());
|
||||
if (i == entries.size() - 1)
|
||||
recordLength += occupiedSize - directorySize;
|
||||
|
||||
printf("* inode: %u", entry.inode.index());
|
||||
printf(", name_len: %u", word(entry.name.length()));
|
||||
printf(", rec_len: %u", word(recordLength));
|
||||
printf(", name: %s\n", entry.name.characters());
|
||||
|
||||
stream << dword(entry.inode.index());
|
||||
stream << word(recordLength);
|
||||
stream << byte(entry.name.length());
|
||||
stream << byte(entry.fileType);
|
||||
stream << entry.name;
|
||||
|
||||
unsigned padding = recordLength - entry.name.length() - 8;
|
||||
printf(" *** pad %u bytes\n", padding);
|
||||
for (unsigned j = 0; j < padding; ++j) {
|
||||
stream << byte(0);
|
||||
}
|
||||
}
|
||||
|
||||
stream.fillToEnd(0);
|
||||
|
||||
#if 0
|
||||
printf("data to write (%u):\n", directoryData.size());
|
||||
for (unsigned i = 0; i < directoryData.size(); ++i) {
|
||||
printf("%02x ", directoryData[i]);
|
||||
if ((i + 1) % 8 == 0)
|
||||
printf(" ");
|
||||
if ((i + 1) % 16 == 0)
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
writeInode({ id(), directoryInode }, directoryData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::inodesPerBlock() const
|
||||
{
|
||||
return EXT2_INODES_PER_BLOCK(&superBlock());
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::inodesPerGroup() const
|
||||
{
|
||||
return EXT2_INODES_PER_GROUP(&superBlock());
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::inodeSize() const
|
||||
{
|
||||
return EXT2_INODE_SIZE(&superBlock());
|
||||
|
||||
}
|
||||
unsigned Ext2FileSystem::blocksPerGroup() const
|
||||
{
|
||||
return EXT2_BLOCKS_PER_GROUP(&superBlock());
|
||||
}
|
||||
|
||||
void Ext2FileSystem::dumpBlockBitmap(unsigned groupIndex) const
|
||||
{
|
||||
ASSERT(groupIndex <= m_blockGroupCount);
|
||||
auto& bgd = blockGroupDescriptor(groupIndex);
|
||||
|
||||
unsigned blocksInGroup = min(blocksPerGroup(), superBlock().s_blocks_count);
|
||||
unsigned blockCount = ceilDiv(blocksInGroup, 8u);
|
||||
|
||||
auto bitmapBlocks = readBlocks(bgd.bg_block_bitmap, blockCount);
|
||||
ASSERT(bitmapBlocks);
|
||||
|
||||
printf("[ext2fs] group[%u] block bitmap (bitmap occupies %u blocks):\n", groupIndex, blockCount);
|
||||
|
||||
auto bitmap = Bitmap::wrap(bitmapBlocks.pointer(), blocksInGroup);
|
||||
for (unsigned i = 0; i < blocksInGroup; ++i) {
|
||||
printf("%c", bitmap.get(i) ? '1' : '0');
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void Ext2FileSystem::dumpInodeBitmap(unsigned groupIndex) const
|
||||
{
|
||||
traverseInodeBitmap(groupIndex, [] (unsigned, const Bitmap& bitmap) {
|
||||
for (unsigned i = 0; i < bitmap.size(); ++i)
|
||||
printf("%c", bitmap.get(i) ? '1' : '0');
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void Ext2FileSystem::traverseInodeBitmap(unsigned groupIndex, F callback) const
|
||||
{
|
||||
ASSERT(groupIndex <= m_blockGroupCount);
|
||||
auto& bgd = blockGroupDescriptor(groupIndex);
|
||||
|
||||
unsigned inodesInGroup = min(inodesPerGroup(), superBlock().s_inodes_count);
|
||||
unsigned blockCount = ceilDiv(inodesInGroup, 8u);
|
||||
|
||||
for (unsigned i = 0; i < blockCount; ++i) {
|
||||
auto block = readBlock(bgd.bg_inode_bitmap + i);
|
||||
ASSERT(block);
|
||||
bool shouldContinue = callback(i * (blockSize() / 8), Bitmap::wrap(block.pointer(), inodesInGroup));
|
||||
if (!shouldContinue)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::setModificationTime(InodeIdentifier inode, dword timestamp)
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
|
||||
auto e2inode = lookupExt2Inode(inode.index());
|
||||
if (!e2inode)
|
||||
return false;
|
||||
|
||||
printf("changing inode %u mtime from %u to %u\n", inode.index(), e2inode->i_mtime, timestamp);
|
||||
e2inode->i_mtime = timestamp;
|
||||
|
||||
return writeExt2Inode(inode.index(), *e2inode);
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::writeExt2Inode(unsigned inode, const ext2_inode& e2inode)
|
||||
{
|
||||
unsigned blockIndex;
|
||||
unsigned offset;
|
||||
auto block = readBlockContainingInode(inode, blockIndex, offset);
|
||||
if (!block)
|
||||
return false;
|
||||
memcpy(reinterpret_cast<ext2_inode*>(block.offsetPointer(offset)), &e2inode, inodeSize());
|
||||
writeBlock(blockIndex, block);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::isDirectoryInode(unsigned inode) const
|
||||
{
|
||||
if (auto e2inode = lookupExt2Inode(inode))
|
||||
return isDirectory(e2inode->i_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::allocateInode(unsigned preferredGroup, unsigned expectedSize)
|
||||
{
|
||||
printf("[ext2fs] allocateInode(preferredGroup: %u, expectedSize: %u)\n", preferredGroup, expectedSize);
|
||||
|
||||
unsigned neededBlocks = ceilDiv(expectedSize, blockSize());
|
||||
|
||||
printf("[ext2fs] minimum needed blocks: %u\n", neededBlocks);
|
||||
|
||||
unsigned groupIndex = 0;
|
||||
|
||||
auto isSuitableGroup = [this, neededBlocks] (unsigned groupIndex) {
|
||||
auto& bgd = blockGroupDescriptor(groupIndex);
|
||||
return bgd.bg_free_inodes_count && bgd.bg_free_blocks_count >= neededBlocks;
|
||||
};
|
||||
|
||||
if (preferredGroup && isSuitableGroup(preferredGroup)) {
|
||||
groupIndex = preferredGroup;
|
||||
} else {
|
||||
for (unsigned i = 1; i <= m_blockGroupCount; ++i) {
|
||||
if (isSuitableGroup(i))
|
||||
groupIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupIndex) {
|
||||
printf("[ext2fs] allocateInode: no suitable group found for new inode with %u blocks needed :(\n", neededBlocks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("[ext2fs] allocateInode: found suitable group [%u] for new inode with %u blocks needed :^)\n", groupIndex, neededBlocks);
|
||||
|
||||
unsigned firstFreeInodeInGroup = 0;
|
||||
traverseInodeBitmap(groupIndex, [&firstFreeInodeInGroup] (unsigned firstInodeInBitmap, const Bitmap& bitmap) {
|
||||
for (unsigned i = 0; i < bitmap.size(); ++i) {
|
||||
if (!bitmap.get(i)) {
|
||||
firstFreeInodeInGroup = firstInodeInBitmap + i;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!firstFreeInodeInGroup) {
|
||||
printf("[ext2fs] firstFreeInodeInGroup returned no inode, despite bgd claiming there are inodes :(\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned inode = firstFreeInodeInGroup;
|
||||
printf("[ext2fs] found suitable inode %u\n", inode);
|
||||
|
||||
// FIXME: allocate blocks if needed!
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
unsigned Ext2FileSystem::groupIndexFromInode(unsigned inode) const
|
||||
{
|
||||
if (!inode)
|
||||
return 0;
|
||||
return (inode - 1) / inodesPerGroup() + 1;
|
||||
}
|
||||
|
||||
bool Ext2FileSystem::setInodeAllocationState(unsigned inode, bool newState)
|
||||
{
|
||||
auto& bgd = blockGroupDescriptor(groupIndexFromInode(inode));
|
||||
|
||||
// Update inode bitmap
|
||||
unsigned inodesPerBitmapBlock = blockSize() * 8;
|
||||
unsigned bitmapBlockIndex = (inode - 1) / inodesPerBitmapBlock;
|
||||
unsigned bitIndex = (inode - 1) % inodesPerBitmapBlock;
|
||||
auto block = readBlock(bgd.bg_inode_bitmap + bitmapBlockIndex);
|
||||
ASSERT(block);
|
||||
auto bitmap = Bitmap::wrap(block.pointer(), block.size());
|
||||
bool currentState = bitmap.get(bitIndex);
|
||||
printf("[ext2fs] setInodeAllocationState(%u) %u -> %u\n", inode, currentState, newState);
|
||||
|
||||
if (currentState == newState)
|
||||
return true;
|
||||
|
||||
bitmap.set(bitIndex, newState);
|
||||
writeBlock(bgd.bg_inode_bitmap + bitmapBlockIndex, block);
|
||||
|
||||
// Update superblock
|
||||
auto& sb = *reinterpret_cast<ext2_super_block*>(m_cachedSuperBlock.pointer());
|
||||
printf("[ext2fs] superblock free inode count %u -> %u\n", sb.s_free_inodes_count, sb.s_free_inodes_count - 1);
|
||||
if (newState)
|
||||
--sb.s_free_inodes_count;
|
||||
else
|
||||
++sb.s_free_inodes_count;
|
||||
writeSuperBlock(sb);
|
||||
|
||||
// Update BGD
|
||||
auto& mutableBGD = const_cast<ext2_group_desc&>(bgd);
|
||||
if (newState)
|
||||
--mutableBGD.bg_free_inodes_count;
|
||||
else
|
||||
++mutableBGD.bg_free_inodes_count;
|
||||
printf("[ext2fs] group free inode count %u -> %u\n", bgd.bg_free_inodes_count, bgd.bg_free_inodes_count - 1);
|
||||
|
||||
unsigned blocksToWrite = ceilDiv(m_blockGroupCount * sizeof(ext2_group_desc), blockSize());
|
||||
unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1;
|
||||
writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cachedBlockGroupDescriptorTable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InodeIdentifier Ext2FileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode)
|
||||
{
|
||||
ASSERT(parentInode.fileSystemID() == id());
|
||||
ASSERT(isDirectoryInode(parentInode.index()));
|
||||
|
||||
//#ifdef EXT2_DEBUG
|
||||
printf("[ext2fs] Adding inode '%s' (mode %o) to parent directory %u:\n", name.characters(), mode, parentInode.index());
|
||||
//#endif
|
||||
|
||||
// NOTE: This doesn't commit the inode allocation just yet!
|
||||
auto inode = allocateInode(0, 0);
|
||||
|
||||
// Try adding it to the directory first, in case the name is already in use.
|
||||
bool success = addInodeToDirectory(parentInode.index(), inode, name);
|
||||
if (!success) {
|
||||
printf("[ext2fs] failed to add inode to directory :(\n");
|
||||
return { };
|
||||
}
|
||||
|
||||
// Looks like we're good, time to update the inode bitmap and group+global inode counters.
|
||||
success = setInodeAllocationState(inode, true);
|
||||
ASSERT(success);
|
||||
|
||||
auto timestamp = time(nullptr);
|
||||
auto e2inode = make<ext2_inode>();
|
||||
memset(e2inode.ptr(), 0, sizeof(ext2_inode));
|
||||
e2inode->i_mode = mode;
|
||||
e2inode->i_uid = 0;
|
||||
e2inode->i_size = 0;
|
||||
e2inode->i_atime = timestamp;
|
||||
e2inode->i_ctime = timestamp;
|
||||
e2inode->i_mtime = timestamp;
|
||||
e2inode->i_dtime = 0;
|
||||
e2inode->i_gid = 0;
|
||||
e2inode->i_links_count = 2;
|
||||
e2inode->i_blocks = 0;
|
||||
e2inode->i_flags = 0;
|
||||
success = writeExt2Inode(inode, *e2inode);
|
||||
ASSERT(success);
|
||||
|
||||
return { id(), inode };
|
||||
}
|
||||
|
65
VirtualFileSystem/Ext2FileSystem.h
Normal file
65
VirtualFileSystem/Ext2FileSystem.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "DeviceBackedFileSystem.h"
|
||||
#include <AK/Buffer.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
|
||||
struct ext2_group_desc;
|
||||
struct ext2_inode;
|
||||
struct ext2_super_block;
|
||||
|
||||
class Ext2FileSystem final : public DeviceBackedFileSystem {
|
||||
public:
|
||||
static RetainPtr<Ext2FileSystem> create(RetainPtr<BlockDevice>);
|
||||
virtual ~Ext2FileSystem() override;
|
||||
|
||||
private:
|
||||
explicit Ext2FileSystem(RetainPtr<BlockDevice>);
|
||||
|
||||
const ext2_super_block& superBlock() const;
|
||||
const ext2_group_desc& blockGroupDescriptor(unsigned groupIndex) const;
|
||||
unsigned firstBlockOfGroup(unsigned groupIndex) const;
|
||||
unsigned inodesPerBlock() const;
|
||||
unsigned inodesPerGroup() const;
|
||||
unsigned blocksPerGroup() const;
|
||||
unsigned inodeSize() const;
|
||||
|
||||
OwnPtr<ext2_inode> lookupExt2Inode(unsigned) const;
|
||||
bool writeExt2Inode(unsigned, const ext2_inode&);
|
||||
ByteBuffer readBlockContainingInode(unsigned inode, unsigned& blockIndex, unsigned& offset) const;
|
||||
|
||||
ByteBuffer readSuperBlock() const;
|
||||
bool writeSuperBlock(const ext2_super_block&);
|
||||
|
||||
virtual bool initialize() override;
|
||||
virtual const char* className() const override;
|
||||
virtual InodeIdentifier rootInode() const override;
|
||||
virtual ByteBuffer readInode(InodeIdentifier) const override;
|
||||
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) override;
|
||||
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) const override;
|
||||
virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
|
||||
virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
|
||||
virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override;
|
||||
|
||||
bool isDirectoryInode(unsigned) const;
|
||||
unsigned allocateInode(unsigned preferredGroup, unsigned expectedSize);
|
||||
unsigned groupIndexFromInode(unsigned) const;
|
||||
|
||||
Vector<unsigned> blockListForInode(const ext2_inode&) const;
|
||||
|
||||
void dumpBlockBitmap(unsigned groupIndex) const;
|
||||
void dumpInodeBitmap(unsigned groupIndex) const;
|
||||
|
||||
template<typename F>
|
||||
void traverseInodeBitmap(unsigned groupIndex, F) const;
|
||||
|
||||
bool addInodeToDirectory(unsigned directoryInode, unsigned inode, const String& name);
|
||||
bool writeDirectoryInode(unsigned directoryInode, Vector<DirectoryEntry>&&);
|
||||
bool setInodeAllocationState(unsigned inode, bool);
|
||||
|
||||
unsigned m_blockGroupCount { 0 };
|
||||
|
||||
mutable ByteBuffer m_cachedSuperBlock;
|
||||
mutable ByteBuffer m_cachedBlockGroupDescriptorTable;
|
||||
};
|
||||
|
82
VirtualFileSystem/FileBackedBlockDevice.cpp
Normal file
82
VirtualFileSystem/FileBackedBlockDevice.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "FileBackedBlockDevice.h"
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
|
||||
//#define FBBD_DEBUG
|
||||
#define IGNORE_FILE_LENGTH // Useful for e.g /dev/hda2
|
||||
|
||||
RetainPtr<FileBackedBlockDevice> FileBackedBlockDevice::create(String&& imagePath, unsigned blockSize)
|
||||
{
|
||||
return adopt(*new FileBackedBlockDevice(std::move(imagePath), blockSize));
|
||||
}
|
||||
|
||||
FileBackedBlockDevice::FileBackedBlockDevice(String&& imagePath, unsigned blockSize)
|
||||
: m_imagePath(std::move(imagePath))
|
||||
, m_blockSize(blockSize)
|
||||
{
|
||||
struct stat st;
|
||||
int result = stat(m_imagePath.characters(), &st);
|
||||
ASSERT(result != -1);
|
||||
m_fileLength = st.st_size;
|
||||
m_file = fopen(m_imagePath.characters(), "r+");
|
||||
}
|
||||
|
||||
FileBackedBlockDevice::~FileBackedBlockDevice()
|
||||
{
|
||||
}
|
||||
|
||||
unsigned FileBackedBlockDevice::blockSize() const
|
||||
{
|
||||
return m_blockSize;
|
||||
}
|
||||
|
||||
bool FileBackedBlockDevice::readBlock(unsigned index, byte* out) const
|
||||
{
|
||||
qword offset = index * m_blockSize;
|
||||
return read(offset, blockSize(), out);
|
||||
}
|
||||
|
||||
bool FileBackedBlockDevice::writeBlock(unsigned index, const byte* data)
|
||||
{
|
||||
qword offset = index * m_blockSize;
|
||||
return write(offset, blockSize(), data);
|
||||
}
|
||||
|
||||
bool FileBackedBlockDevice::read(qword offset, unsigned length, byte* out) const
|
||||
{
|
||||
#ifndef IGNORE_FILE_LENGTH
|
||||
if (offset + length >= m_fileLength)
|
||||
return false;
|
||||
#endif
|
||||
#ifdef FBBD_DEBUG
|
||||
printf("[FileBackedBlockDevice] Read device @ offset %llx, length %u\n", offset, length);
|
||||
#endif
|
||||
fseeko(m_file, offset, SEEK_SET);
|
||||
unsigned nread = fread(out, sizeof(byte), length, m_file);
|
||||
ASSERT(nread == length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBackedBlockDevice::write(qword offset, unsigned length, const byte* data)
|
||||
{
|
||||
#ifndef IGNORE_FILE_LENGTH
|
||||
if (offset + length >= m_fileLength)
|
||||
return false;
|
||||
#endif
|
||||
#ifdef FBBD_DEBUG
|
||||
printf("[FileBackedBlockDevice] Write device @ offset %llx, length %u\n", offset, length);
|
||||
#endif
|
||||
fseeko(m_file, offset, SEEK_SET);
|
||||
// size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
unsigned nwritten = fwrite(data, sizeof(byte), length, m_file);
|
||||
ASSERT(nwritten == length);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* FileBackedBlockDevice::className() const
|
||||
{
|
||||
return "FileBackedBlockDevice";
|
||||
}
|
||||
|
32
VirtualFileSystem/FileBackedBlockDevice.h
Normal file
32
VirtualFileSystem/FileBackedBlockDevice.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include <AK/RetainPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
class FileBackedBlockDevice final : public BlockDevice {
|
||||
public:
|
||||
static RetainPtr<FileBackedBlockDevice> create(String&& imagePath, unsigned blockSize);
|
||||
virtual ~FileBackedBlockDevice() override;
|
||||
|
||||
bool isValid() const { return m_file; }
|
||||
|
||||
virtual unsigned blockSize() const override;
|
||||
virtual bool readBlock(unsigned index, byte* out) const override;
|
||||
virtual bool writeBlock(unsigned index, const byte*) override;
|
||||
virtual bool read(qword offset, unsigned length, byte* out) const override;
|
||||
virtual bool write(qword offset, unsigned length, const byte* data) override;
|
||||
|
||||
private:
|
||||
virtual const char* className() const override;
|
||||
|
||||
FileBackedBlockDevice(String&& imagePath, unsigned blockSize);
|
||||
|
||||
String m_imagePath;
|
||||
FILE* m_file { nullptr };
|
||||
qword m_fileLength { 0 };
|
||||
unsigned m_blockSize { 0 };
|
||||
};
|
||||
|
17
VirtualFileSystem/FileHandle.cpp
Normal file
17
VirtualFileSystem/FileHandle.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "FileHandle.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
FileHandle::FileHandle(RetainPtr<VirtualFileSystem::Node>&& vnode)
|
||||
: m_vnode(std::move(vnode))
|
||||
{
|
||||
}
|
||||
|
||||
FileHandle::~FileHandle()
|
||||
{
|
||||
}
|
||||
|
||||
ByteBuffer FileHandle::read() const
|
||||
{
|
||||
return m_vnode->fileSystem()->readInode(m_vnode->inode);
|
||||
}
|
||||
|
18
VirtualFileSystem/FileHandle.h
Normal file
18
VirtualFileSystem/FileHandle.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "VirtualFileSystem.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
|
||||
class FileHandle {
|
||||
public:
|
||||
explicit FileHandle(RetainPtr<VirtualFileSystem::Node>&&);
|
||||
~FileHandle();
|
||||
|
||||
ByteBuffer read() const;
|
||||
|
||||
private:
|
||||
friend class VirtualFileSystem;
|
||||
|
||||
RetainPtr<VirtualFileSystem::Node> m_vnode;
|
||||
};
|
||||
|
45
VirtualFileSystem/FileSystem.cpp
Normal file
45
VirtualFileSystem/FileSystem.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include "FileSystem.h"
|
||||
|
||||
static dword s_lastFileSystemID = 0;
|
||||
|
||||
static HashMap<dword, FileSystem*>& fileSystems()
|
||||
{
|
||||
static auto* map = new HashMap<dword, FileSystem*>();
|
||||
return *map;
|
||||
}
|
||||
|
||||
FileSystem::FileSystem()
|
||||
: m_id(++s_lastFileSystemID)
|
||||
{
|
||||
fileSystems().set(m_id, this);
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem()
|
||||
{
|
||||
// FIXME: Needs HashMap::remove()!
|
||||
//fileSystems().remove(m_id);
|
||||
}
|
||||
|
||||
FileSystem* FileSystem::fromID(dword id)
|
||||
{
|
||||
auto it = fileSystems().find(id);
|
||||
if (it != fileSystems().end())
|
||||
return (*it).value;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InodeIdentifier FileSystem::childOfDirectoryInodeWithName(InodeIdentifier inode, const String& name)
|
||||
{
|
||||
InodeIdentifier foundInode;
|
||||
enumerateDirectoryInode(inode, [&] (const DirectoryEntry& entry) {
|
||||
if (entry.name == name) {
|
||||
foundInode = entry.inode;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return foundInode;
|
||||
}
|
||||
|
81
VirtualFileSystem/FileSystem.h
Normal file
81
VirtualFileSystem/FileSystem.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include "InodeIdentifier.h"
|
||||
#include "InodeMetadata.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Retainable.h>
|
||||
#include <AK/RetainPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <functional>
|
||||
|
||||
static const dword mepoch = 476763780;
|
||||
|
||||
class FileSystem : public Retainable<FileSystem> {
|
||||
public:
|
||||
virtual ~FileSystem();
|
||||
|
||||
dword id() const { return m_id; }
|
||||
static FileSystem* fromID(dword);
|
||||
|
||||
virtual bool initialize() = 0;
|
||||
virtual const char* className() const = 0;
|
||||
virtual InodeIdentifier rootInode() const = 0;
|
||||
virtual ByteBuffer readInode(InodeIdentifier) const = 0;
|
||||
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) = 0;
|
||||
virtual InodeMetadata inodeMetadata(InodeIdentifier) const = 0;
|
||||
|
||||
struct DirectoryEntry {
|
||||
String name;
|
||||
InodeIdentifier inode;
|
||||
byte fileType { 0 };
|
||||
};
|
||||
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) const = 0;
|
||||
|
||||
virtual bool setModificationTime(InodeIdentifier, dword timestamp) = 0;
|
||||
virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) = 0;
|
||||
|
||||
InodeIdentifier childOfDirectoryInodeWithName(InodeIdentifier, const String& name);
|
||||
|
||||
protected:
|
||||
FileSystem();
|
||||
|
||||
private:
|
||||
dword m_id { 0 };
|
||||
};
|
||||
|
||||
inline FileSystem* InodeIdentifier::fileSystem()
|
||||
{
|
||||
return FileSystem::fromID(m_fileSystemID);
|
||||
}
|
||||
|
||||
inline const FileSystem* InodeIdentifier::fileSystem() const
|
||||
{
|
||||
return FileSystem::fromID(m_fileSystemID);
|
||||
}
|
||||
|
||||
inline InodeMetadata InodeIdentifier::metadata() const
|
||||
{
|
||||
if (!isValid())
|
||||
return InodeMetadata();
|
||||
return fileSystem()->inodeMetadata(*this);
|
||||
}
|
||||
|
||||
inline bool InodeIdentifier::isRootInode() const
|
||||
{
|
||||
return (*this) == fileSystem()->rootInode();
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
||||
template<>
|
||||
struct Traits<InodeIdentifier> {
|
||||
// FIXME: This is a shitty hash.
|
||||
static unsigned hash(const InodeIdentifier& inode) { return Traits<unsigned>::hash(inode.fileSystemID()) + Traits<unsigned>::hash(inode.index()); }
|
||||
static void dump(const InodeIdentifier& inode) { printf("%02u:%08u", inode.fileSystemID(), inode.index()); }
|
||||
};
|
||||
|
||||
}
|
||||
|
9
VirtualFileSystem/InodeIdentifier.cpp
Normal file
9
VirtualFileSystem/InodeIdentifier.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "InodeIdentifier.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
ByteBuffer InodeIdentifier::readEntireFile() const
|
||||
{
|
||||
if (!fileSystem())
|
||||
return { };
|
||||
return fileSystem()->readInode(*this);
|
||||
}
|
40
VirtualFileSystem/InodeIdentifier.h
Normal file
40
VirtualFileSystem/InodeIdentifier.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
class FileSystem;
|
||||
struct InodeMetadata;
|
||||
|
||||
class InodeIdentifier {
|
||||
public:
|
||||
InodeIdentifier() { }
|
||||
InodeIdentifier(dword fileSystemID, dword inode)
|
||||
: m_fileSystemID(fileSystemID)
|
||||
, m_index(inode)
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const { return m_fileSystemID != 0 && m_index != 0; }
|
||||
|
||||
dword fileSystemID() const { return m_fileSystemID; }
|
||||
dword index() const { return m_index; }
|
||||
|
||||
FileSystem* fileSystem();
|
||||
const FileSystem* fileSystem() const;
|
||||
|
||||
bool operator==(const InodeIdentifier& other) const
|
||||
{
|
||||
return m_fileSystemID == other.m_fileSystemID && m_index == other.m_index;
|
||||
}
|
||||
|
||||
InodeMetadata metadata() const;
|
||||
bool isRootInode() const;
|
||||
|
||||
ByteBuffer readEntireFile() const;
|
||||
|
||||
private:
|
||||
dword m_fileSystemID { 0 };
|
||||
dword m_index { 0 };
|
||||
};
|
||||
|
42
VirtualFileSystem/InodeMetadata.h
Normal file
42
VirtualFileSystem/InodeMetadata.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "InodeIdentifier.h"
|
||||
|
||||
inline bool isDirectory(word mode) { return (mode & 0170000) == 0040000; }
|
||||
inline bool isCharacterDevice(word mode) { return (mode & 0170000) == 0020000; }
|
||||
inline bool isBlockDevice(word mode) { return (mode & 0170000) == 0060000; }
|
||||
inline bool isRegularFile(word mode) { return (mode & 0170000) == 0100000; }
|
||||
inline bool isFIFO(word mode) { return (mode & 0170000) == 0010000; }
|
||||
inline bool isSymbolicLink(word mode) { return (mode & 0170000) == 0120000; }
|
||||
inline bool isSocket(word mode) { return (mode & 0170000) == 0140000; }
|
||||
inline bool isSticky(word mode) { return mode & 01000; }
|
||||
inline bool isSetUID(word mode) { return mode & 04000; }
|
||||
inline bool isSetGID(word mode) { return mode & 02000; }
|
||||
|
||||
struct InodeMetadata {
|
||||
bool isValid() const { return inode.isValid(); }
|
||||
|
||||
bool isDirectory() const { return ::isDirectory(mode); }
|
||||
bool isCharacterDevice() const { return ::isCharacterDevice(mode); }
|
||||
bool isBlockDevice() const { return ::isBlockDevice(mode); }
|
||||
bool isRegularFile() const { return ::isRegularFile(mode); }
|
||||
bool isFIFO() const { return ::isFIFO(mode); }
|
||||
bool isSymbolicLink() const { return ::isSymbolicLink(mode); }
|
||||
bool isSocket() const { return ::isSocket(mode); }
|
||||
bool isSticky() const { return ::isSticky(mode); }
|
||||
bool isSetUID() const { return ::isSetUID(mode); }
|
||||
bool isSetGID() const { return ::isSetGID(mode); }
|
||||
|
||||
InodeIdentifier inode;
|
||||
dword size { 0 };
|
||||
word mode { 0 };
|
||||
dword uid { 0 };
|
||||
dword gid { 0 };
|
||||
dword linkCount { 0 };
|
||||
time_t atime { 0 };
|
||||
time_t ctime { 0 };
|
||||
time_t mtime { 0 };
|
||||
time_t dtime { 0 };
|
||||
};
|
||||
|
||||
|
39
VirtualFileSystem/Makefile
Normal file
39
VirtualFileSystem/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
PROGRAM = test
|
||||
|
||||
AK_OBJS = \
|
||||
../AK/String.o \
|
||||
../AK/StringImpl.o \
|
||||
../AK/MappedFile.o \
|
||||
../AK/TemporaryFile.o \
|
||||
../AK/SimpleMalloc.o \
|
||||
../AK/kmalloc.o
|
||||
|
||||
VFS_OBJS = \
|
||||
BlockDevice.o \
|
||||
FileBackedBlockDevice.o \
|
||||
FileSystem.o \
|
||||
Ext2FileSystem.o \
|
||||
VirtualFileSystem.o \
|
||||
FileHandle.o \
|
||||
DeviceBackedFileSystem.o \
|
||||
SyntheticFileSystem.o \
|
||||
InodeIdentifier.o \
|
||||
test.o
|
||||
|
||||
OBJS = $(AK_OBJS) $(VFS_OBJS)
|
||||
|
||||
CXXFLAGS = -std=c++17 -Os -W -Wall -Wextra -Wconversion -I. -I.. -ggdb3 -Wno-class-memaccess
|
||||
|
||||
#test.o: BlockDevice.h FileBackedBlockDevice.h FileSystem.h Ext2FileSystem.h VirtualFileSystem.h FileHandle.h
|
||||
|
||||
all: $(PROGRAM)
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(CXXFLAGS) -o $@ -c $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(PROGRAM)
|
||||
|
||||
$(PROGRAM): $(OBJS)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
123
VirtualFileSystem/SyntheticFileSystem.cpp
Normal file
123
VirtualFileSystem/SyntheticFileSystem.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "SyntheticFileSystem.h"
|
||||
|
||||
RetainPtr<SyntheticFileSystem> SyntheticFileSystem::create()
|
||||
{
|
||||
return adopt(*new SyntheticFileSystem);
|
||||
}
|
||||
|
||||
SyntheticFileSystem::SyntheticFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
SyntheticFileSystem::~SyntheticFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
bool SyntheticFileSystem::initialize()
|
||||
{
|
||||
// Add a File for the root directory.
|
||||
// FIXME: This needs work.
|
||||
auto rootDir = make<File>();
|
||||
rootDir->metadata.inode = { id(), 1 };
|
||||
rootDir->metadata.mode = 0040555;
|
||||
rootDir->metadata.uid = 0;
|
||||
rootDir->metadata.gid = 0;
|
||||
rootDir->metadata.size = 0;
|
||||
rootDir->metadata.mtime = mepoch;
|
||||
m_files.append(std::move(rootDir));
|
||||
|
||||
addFile(createTextFile("file", "I'm a synthetic file!\n"));
|
||||
addFile(createTextFile("message", "Hey! This isn't my bottle!\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto SyntheticFileSystem::createTextFile(String&& name, String&& text) -> OwnPtr<File>
|
||||
{
|
||||
auto file = make<File>();
|
||||
file->data = text.toByteBuffer();
|
||||
file->name = std::move(name);
|
||||
file->metadata.size = file->data.size();
|
||||
file->metadata.uid = 100;
|
||||
file->metadata.gid = 200;
|
||||
file->metadata.mode = 0100644;
|
||||
file->metadata.mtime = mepoch;
|
||||
return file;
|
||||
}
|
||||
|
||||
void SyntheticFileSystem::addFile(OwnPtr<File>&& file)
|
||||
{
|
||||
ASSERT(file);
|
||||
file->metadata.inode = { id(), m_files.size() + 1 };
|
||||
m_files.append(std::move(file));
|
||||
}
|
||||
|
||||
const char* SyntheticFileSystem::className() const
|
||||
{
|
||||
return "synthfs";
|
||||
}
|
||||
|
||||
InodeIdentifier SyntheticFileSystem::rootInode() const
|
||||
{
|
||||
return { id(), 1 };
|
||||
}
|
||||
|
||||
ByteBuffer SyntheticFileSystem::readInode(InodeIdentifier inode) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
#ifdef SYNTHFS_DEBUG
|
||||
printf("[synthfs] readInode %u\n", inode.index());
|
||||
#endif
|
||||
ASSERT(inode.index() != 1);
|
||||
ASSERT(inode.index() <= m_files.size());
|
||||
return m_files[inode.index() - 1]->data;
|
||||
}
|
||||
|
||||
bool SyntheticFileSystem::enumerateDirectoryInode(InodeIdentifier inode, std::function<bool(const DirectoryEntry&)> callback) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
#ifdef SYNTHFS_DEBUG
|
||||
printf("[synthfs] enumerateDirectoryInode %u\n", inode.index());
|
||||
#endif
|
||||
if (inode.index() != 1)
|
||||
return false;
|
||||
|
||||
callback({ ".", m_files[0]->metadata.inode });
|
||||
callback({ "..", m_files[0]->metadata.inode });
|
||||
|
||||
for (unsigned i = 1; i < m_files.size(); ++i)
|
||||
callback({ m_files[i]->name, m_files[i]->metadata.inode });
|
||||
return true;
|
||||
}
|
||||
|
||||
InodeMetadata SyntheticFileSystem::inodeMetadata(InodeIdentifier inode) const
|
||||
{
|
||||
ASSERT(inode.fileSystemID() == id());
|
||||
#ifdef SYNTHFS_DEBUG
|
||||
printf("[synthfs] inodeMetadata(%u)\n", inode.index);
|
||||
#endif
|
||||
if (inode.index() == 0 || inode.index() > m_files.size())
|
||||
return InodeMetadata();
|
||||
return m_files[inode.index() - 1]->metadata;
|
||||
}
|
||||
|
||||
bool SyntheticFileSystem::setModificationTime(InodeIdentifier, dword timestamp)
|
||||
{
|
||||
(void) timestamp;
|
||||
printf("FIXME: Implement SyntheticFileSystem::setModificationTime().\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
InodeIdentifier SyntheticFileSystem::createInode(InodeIdentifier parentInode, const String& name, word mode)
|
||||
{
|
||||
(void) parentInode;
|
||||
(void) name;
|
||||
(void) mode;
|
||||
printf("FIXME: Implement SyntheticFileSystem::createDirectoryInode().\n");
|
||||
return { };
|
||||
}
|
||||
|
||||
bool SyntheticFileSystem::writeInode(InodeIdentifier, const ByteBuffer&)
|
||||
{
|
||||
printf("FIXME: Implement SyntheticFileSystem::writeInode().\n");
|
||||
return false;
|
||||
}
|
35
VirtualFileSystem/SyntheticFileSystem.h
Normal file
35
VirtualFileSystem/SyntheticFileSystem.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include <AK/HashMap.h>
|
||||
|
||||
class SyntheticFileSystem final : public FileSystem {
|
||||
public:
|
||||
virtual ~SyntheticFileSystem() override;
|
||||
static RetainPtr<SyntheticFileSystem> create();
|
||||
|
||||
virtual bool initialize() override;
|
||||
virtual const char* className() const override;
|
||||
virtual InodeIdentifier rootInode() const override;
|
||||
virtual ByteBuffer readInode(InodeIdentifier) const override;
|
||||
virtual bool writeInode(InodeIdentifier, const ByteBuffer&) override;
|
||||
virtual bool enumerateDirectoryInode(InodeIdentifier, std::function<bool(const DirectoryEntry&)>) const override;
|
||||
virtual InodeMetadata inodeMetadata(InodeIdentifier) const override;
|
||||
virtual bool setModificationTime(InodeIdentifier, dword timestamp) override;
|
||||
virtual InodeIdentifier createInode(InodeIdentifier parentInode, const String& name, word mode) override;
|
||||
|
||||
private:
|
||||
SyntheticFileSystem();
|
||||
|
||||
struct File {
|
||||
String name;
|
||||
InodeMetadata metadata;
|
||||
ByteBuffer data;
|
||||
};
|
||||
|
||||
OwnPtr<File> createTextFile(String&& name, String&& text);
|
||||
void addFile(OwnPtr<File>&&);
|
||||
|
||||
Vector<OwnPtr<File>> m_files;
|
||||
};
|
||||
|
408
VirtualFileSystem/VirtualFileSystem.cpp
Normal file
408
VirtualFileSystem/VirtualFileSystem.cpp
Normal file
@ -0,0 +1,408 @@
|
||||
#include "VirtualFileSystem.h"
|
||||
#include "FileHandle.h"
|
||||
#include "FileSystem.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "kmalloc.h"
|
||||
|
||||
//#define VFS_DEBUG
|
||||
|
||||
VirtualFileSystem::VirtualFileSystem()
|
||||
{
|
||||
m_maxNodeCount = 16;
|
||||
m_nodes = reinterpret_cast<Node*>(kmalloc(sizeof(Node) * maxNodeCount()));
|
||||
memset(m_nodes, 0, sizeof(Node) * maxNodeCount());
|
||||
|
||||
for (unsigned i = 0; i < m_maxNodeCount; ++i)
|
||||
m_nodeFreeList.append(&m_nodes[i]);
|
||||
}
|
||||
|
||||
VirtualFileSystem::~VirtualFileSystem()
|
||||
{
|
||||
printf("[VFS] ~VirtualFileSystem with %u nodes allocated\n", allocatedNodeCount());
|
||||
}
|
||||
|
||||
auto VirtualFileSystem::makeNode(InodeIdentifier inode) -> RetainPtr<Node>
|
||||
{
|
||||
auto metadata = inode.metadata();
|
||||
if (!metadata.isValid())
|
||||
return nullptr;
|
||||
|
||||
auto vnode = allocateNode();
|
||||
ASSERT(vnode);
|
||||
|
||||
FileSystem* fileSystem = inode.fileSystem();
|
||||
fileSystem->retain();
|
||||
|
||||
vnode->inode = inode;
|
||||
|
||||
#ifdef VFS_DEBUG
|
||||
printf("makeNode: inode=%u, size=%u, mode=%o, uid=%u, gid=%u\n", inode.index(), metadata.size, metadata.mode, metadata.uid, metadata.gid);
|
||||
#endif
|
||||
|
||||
m_inode2vnode.set(inode, vnode.ptr());
|
||||
return vnode;
|
||||
}
|
||||
|
||||
auto VirtualFileSystem::getOrCreateNode(InodeIdentifier inode) -> RetainPtr<Node>
|
||||
{
|
||||
auto it = m_inode2vnode.find(inode);
|
||||
if (it != m_inode2vnode.end())
|
||||
return (*it).value;
|
||||
return makeNode(inode);
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::mount(RetainPtr<FileSystem>&& fileSystem, const String& path)
|
||||
{
|
||||
ASSERT(fileSystem);
|
||||
|
||||
auto inode = resolvePath(path);
|
||||
if (!inode.isValid()) {
|
||||
printf("[VFS] mount can't resolve mount point '%s'\n", path.characters());
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("mounting %s{%p} at %s (inode: %u)\n", fileSystem->className(), fileSystem.ptr(), path.characters(), inode.index());
|
||||
// FIXME: check that this is not already a mount point
|
||||
auto mount = make<Mount>(inode, std::move(fileSystem));
|
||||
m_mounts.append(std::move(mount));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::mountRoot(RetainPtr<FileSystem>&& fileSystem)
|
||||
{
|
||||
if (m_rootNode) {
|
||||
printf("[VFS] mountRoot can't mount another root\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto mount = make<Mount>(InodeIdentifier(), std::move(fileSystem));
|
||||
|
||||
auto node = makeNode(mount->guest());
|
||||
if (!node->inUse()) {
|
||||
printf("[VFS] root inode for / is not in use :(\n");
|
||||
return false;
|
||||
}
|
||||
if (!node->inode.metadata().isDirectory()) {
|
||||
printf("[VFS] root inode for / is not in use :(\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_rootNode = std::move(node);
|
||||
|
||||
printf("[VFS] mountRoot mounted %s{%p}\n",
|
||||
m_rootNode->fileSystem()->className(),
|
||||
m_rootNode->fileSystem());
|
||||
|
||||
m_mounts.append(std::move(mount));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto VirtualFileSystem::allocateNode() -> RetainPtr<Node>
|
||||
{
|
||||
if (m_nodeFreeList.isEmpty()) {
|
||||
printf("[VFS] allocateNode has no nodes left\n");
|
||||
return nullptr;
|
||||
}
|
||||
auto* node = m_nodeFreeList.takeLast();
|
||||
ASSERT(node->retainCount == 0);
|
||||
node->retainCount = 1;
|
||||
node->vfs = this;
|
||||
return adopt(*node);
|
||||
}
|
||||
|
||||
void VirtualFileSystem::freeNode(Node* node)
|
||||
{
|
||||
ASSERT(node);
|
||||
ASSERT(node->inUse());
|
||||
node->inode.fileSystem()->release();
|
||||
node->inode = InodeIdentifier();
|
||||
m_nodeFreeList.append(std::move(node));
|
||||
// FIXME: Need HashMap::remove.
|
||||
//m_inode2vnode.remove(node);
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::isDirectory(const String& path)
|
||||
{
|
||||
auto inode = resolvePath(path);
|
||||
if (!inode.isValid())
|
||||
return false;
|
||||
|
||||
return inode.metadata().isDirectory();
|
||||
}
|
||||
|
||||
auto VirtualFileSystem::findMountForHost(InodeIdentifier inode) -> Mount*
|
||||
{
|
||||
for (auto& mount : m_mounts) {
|
||||
if (mount->host() == inode)
|
||||
return mount.ptr();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto VirtualFileSystem::findMountForGuest(InodeIdentifier inode) -> Mount*
|
||||
{
|
||||
for (auto& mount : m_mounts) {
|
||||
if (mount->guest() == inode)
|
||||
return mount.ptr();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::isRoot(InodeIdentifier inode) const
|
||||
{
|
||||
return inode == m_rootNode->inode;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void VirtualFileSystem::enumerateDirectoryInode(InodeIdentifier directoryInode, F func)
|
||||
{
|
||||
if (!directoryInode.isValid())
|
||||
return;
|
||||
|
||||
directoryInode.fileSystem()->enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
|
||||
InodeIdentifier resolvedInode;
|
||||
if (auto mount = findMountForHost(entry.inode))
|
||||
resolvedInode = mount->guest();
|
||||
else
|
||||
resolvedInode = entry.inode;
|
||||
|
||||
if (directoryInode.isRootInode() && !isRoot(directoryInode) && entry.name == "..") {
|
||||
auto mount = findMountForGuest(entry.inode);
|
||||
ASSERT(mount);
|
||||
resolvedInode = mount->host();
|
||||
}
|
||||
func({ entry.name, resolvedInode });
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VirtualFileSystem::listDirectory(const String& path)
|
||||
{
|
||||
auto directoryInode = resolvePath(path);
|
||||
if (!directoryInode.isValid())
|
||||
return;
|
||||
|
||||
printf("[VFS] ls %s -> %s %02u:%08u\n", path.characters(), directoryInode.fileSystem()->className(), directoryInode.fileSystemID(), directoryInode.index());
|
||||
enumerateDirectoryInode(directoryInode, [&] (const FileSystem::DirectoryEntry& entry) {
|
||||
const char* nameColorBegin = "";
|
||||
const char* nameColorEnd = "";
|
||||
auto metadata = entry.inode.metadata();
|
||||
ASSERT(metadata.isValid());
|
||||
if (metadata.isDirectory()) {
|
||||
nameColorBegin = "\033[34;1m";
|
||||
nameColorEnd = "\033[0m";
|
||||
} else if (metadata.isSymbolicLink()) {
|
||||
nameColorBegin = "\033[36;1m";
|
||||
nameColorEnd = "\033[0m";
|
||||
}
|
||||
if (metadata.isSticky()) {
|
||||
nameColorBegin = "\033[42;30m";
|
||||
nameColorEnd = "\033[0m";
|
||||
}
|
||||
printf("%02u:%08u ",
|
||||
metadata.inode.fileSystemID(),
|
||||
metadata.inode.index());
|
||||
|
||||
if (metadata.isDirectory())
|
||||
printf("d");
|
||||
else if (metadata.isSymbolicLink())
|
||||
printf("l");
|
||||
else if (metadata.isBlockDevice())
|
||||
printf("b");
|
||||
else if (metadata.isCharacterDevice())
|
||||
printf("c");
|
||||
else if (metadata.isSocket())
|
||||
printf("s");
|
||||
else if (metadata.isFIFO())
|
||||
printf("f");
|
||||
else if (metadata.isRegularFile())
|
||||
printf("-");
|
||||
else
|
||||
printf("?");
|
||||
|
||||
printf("%c%c%c%c%c%c%c%c",
|
||||
metadata.mode & 00400 ? 'r' : '-',
|
||||
metadata.mode & 00200 ? 'w' : '-',
|
||||
metadata.mode & 00100 ? 'x' : '-',
|
||||
metadata.mode & 00040 ? 'r' : '-',
|
||||
metadata.mode & 00020 ? 'w' : '-',
|
||||
metadata.mode & 00010 ? 'x' : '-',
|
||||
metadata.mode & 00004 ? 'r' : '-',
|
||||
metadata.mode & 00002 ? 'w' : '-'
|
||||
);
|
||||
|
||||
if (metadata.isSticky())
|
||||
printf("t");
|
||||
else
|
||||
printf("%c", metadata.mode & 00001 ? 'x' : '-');
|
||||
|
||||
printf("%12u ", metadata.size);
|
||||
|
||||
printf("\033[30;1m");
|
||||
auto tm = *localtime(&metadata.mtime);
|
||||
printf("%04u-%02u-%02u %02u:%02u:%02u ",
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec);
|
||||
printf("\033[0m");
|
||||
|
||||
printf("%s%s%s",
|
||||
nameColorBegin,
|
||||
entry.name.characters(),
|
||||
nameColorEnd);
|
||||
|
||||
if (metadata.isDirectory()) {
|
||||
printf("/");
|
||||
} else if (metadata.isSymbolicLink()) {
|
||||
auto symlinkContents = directoryInode.fileSystem()->readInode(metadata.inode);
|
||||
printf(" -> %s", String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
|
||||
}
|
||||
printf("\n");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VirtualFileSystem::listDirectoryRecursively(const String& path)
|
||||
{
|
||||
auto directory = resolvePath(path);
|
||||
if (!directory.isValid())
|
||||
return;
|
||||
|
||||
printf("%s\n", path.characters());
|
||||
|
||||
enumerateDirectoryInode(directory, [&] (const FileSystem::DirectoryEntry& entry) {
|
||||
auto metadata = entry.inode.metadata();
|
||||
if (metadata.isDirectory()) {
|
||||
if (entry.name != "." && entry.name != "..") {
|
||||
char buf[4096];
|
||||
sprintf(buf, "%s/%s", path.characters(), entry.name.characters());
|
||||
listDirectoryRecursively(buf);
|
||||
}
|
||||
} else {
|
||||
printf("%s/%s\n", path.characters(), entry.name.characters());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool VirtualFileSystem::touch(const String& path)
|
||||
{
|
||||
auto inode = resolvePath(path);
|
||||
if (!inode.isValid())
|
||||
return false;
|
||||
return inode.fileSystem()->setModificationTime(inode, time(nullptr));
|
||||
}
|
||||
|
||||
OwnPtr<FileHandle> VirtualFileSystem::open(const String& path)
|
||||
{
|
||||
auto inode = resolvePath(path);
|
||||
if (!inode.isValid())
|
||||
return nullptr;
|
||||
auto vnode = getOrCreateNode(inode);
|
||||
if (!vnode)
|
||||
return nullptr;
|
||||
return make<FileHandle>(std::move(vnode));
|
||||
}
|
||||
|
||||
OwnPtr<FileHandle> VirtualFileSystem::create(const String& path)
|
||||
{
|
||||
// FIXME: Do the real thing, not just this fake thing!
|
||||
m_rootNode->fileSystem()->createInode(m_rootNode->fileSystem()->rootInode(), "empty", 0100644);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InodeIdentifier VirtualFileSystem::resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode)
|
||||
{
|
||||
auto symlinkContents = symlinkInode.readEntireFile();
|
||||
if (!symlinkContents)
|
||||
return { };
|
||||
char buf[4096];
|
||||
sprintf(buf, "/%s/%s", basePath.characters(), String((const char*)symlinkContents.pointer(), symlinkContents.size()).characters());
|
||||
return resolvePath(buf);
|
||||
}
|
||||
|
||||
InodeIdentifier VirtualFileSystem::resolvePath(const String& path)
|
||||
{
|
||||
auto parts = path.split('/');
|
||||
InodeIdentifier inode = m_rootNode->inode;
|
||||
|
||||
for (unsigned i = 0; i < parts.size(); ++i) {
|
||||
auto& part = parts[i];
|
||||
auto metadata = inode.metadata();
|
||||
if (!metadata.isValid()) {
|
||||
#ifdef VFS_DEBUG
|
||||
printf("invalid metadata\n");
|
||||
#endif
|
||||
return InodeIdentifier();
|
||||
}
|
||||
if (!metadata.isDirectory()) {
|
||||
#ifdef VFS_DEBUG
|
||||
printf("not directory\n");
|
||||
#endif
|
||||
return InodeIdentifier();
|
||||
}
|
||||
inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, part);
|
||||
if (!inode.isValid()) {
|
||||
#ifdef VFS_DEBUG
|
||||
printf("bad child\n");
|
||||
#endif
|
||||
return InodeIdentifier();
|
||||
}
|
||||
#ifdef VFS_DEBUG
|
||||
printf("<%s> %02u:%08u\n", part.characters(), inode.fileSystemID(), inode.index());
|
||||
#endif
|
||||
if (auto mount = findMountForHost(inode)) {
|
||||
#ifdef VFS_DEBUG
|
||||
printf(" -- is host\n");
|
||||
#endif
|
||||
inode = mount->guest();
|
||||
}
|
||||
if (inode.isRootInode() && !isRoot(inode) && part == "..") {
|
||||
#ifdef VFS_DEBUG
|
||||
printf(" -- is guest\n");
|
||||
#endif
|
||||
auto mount = findMountForGuest(inode);
|
||||
inode = mount->host();
|
||||
inode = inode.fileSystem()->childOfDirectoryInodeWithName(inode, "..");
|
||||
}
|
||||
metadata = inode.metadata();
|
||||
if (metadata.isSymbolicLink()) {
|
||||
char buf[4096] = "";
|
||||
char* p = buf;
|
||||
for (unsigned j = 0; j < i; ++j) {
|
||||
p += sprintf(p, "/%s", parts[j].characters());
|
||||
}
|
||||
inode = resolveSymbolicLink(buf, inode);
|
||||
if (!inode.isValid()) {
|
||||
printf("Symbolic link resolution failed :(\n");
|
||||
return { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inode;
|
||||
}
|
||||
|
||||
void VirtualFileSystem::Node::retain()
|
||||
{
|
||||
++retainCount;
|
||||
}
|
||||
|
||||
void VirtualFileSystem::Node::release()
|
||||
{
|
||||
ASSERT(retainCount);
|
||||
if (--retainCount == 0) {
|
||||
vfs->freeNode(this);
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFileSystem::Mount::Mount(InodeIdentifier host, RetainPtr<FileSystem>&& guestFileSystem)
|
||||
: m_host(host)
|
||||
, m_guest(guestFileSystem->rootInode())
|
||||
, m_fileSystem(std::move(guestFileSystem))
|
||||
{
|
||||
}
|
||||
|
93
VirtualFileSystem/VirtualFileSystem.h
Normal file
93
VirtualFileSystem/VirtualFileSystem.h
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/RetainPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include "InodeIdentifier.h"
|
||||
|
||||
class FileHandle;
|
||||
class FileSystem;
|
||||
|
||||
class VirtualFileSystem {
|
||||
public:
|
||||
struct Node {
|
||||
InodeIdentifier inode;
|
||||
|
||||
bool inUse() const { return inode.isValid(); }
|
||||
|
||||
void retain();
|
||||
void release();
|
||||
|
||||
FileSystem* fileSystem() { return inode.fileSystem(); }
|
||||
const FileSystem* fileSystem() const { return inode.fileSystem(); }
|
||||
|
||||
private:
|
||||
friend class VirtualFileSystem;
|
||||
VirtualFileSystem* vfs { nullptr };
|
||||
unsigned retainCount { 0 };
|
||||
};
|
||||
|
||||
VirtualFileSystem();
|
||||
~VirtualFileSystem();
|
||||
|
||||
bool isDirectory(const String& path);
|
||||
void listDirectory(const String& path);
|
||||
void listDirectoryRecursively(const String& path);
|
||||
|
||||
unsigned maxNodeCount() const { return m_maxNodeCount; }
|
||||
unsigned allocatedNodeCount() const { return m_maxNodeCount - m_nodeFreeList.size(); }
|
||||
|
||||
Node* root() { return m_rootNode.ptr(); }
|
||||
const Node* root() const { return m_rootNode.ptr(); }
|
||||
|
||||
bool mountRoot(RetainPtr<FileSystem>&&);
|
||||
bool mount(RetainPtr<FileSystem>&&, const String& path);
|
||||
|
||||
OwnPtr<FileHandle> open(const String& path);
|
||||
OwnPtr<FileHandle> create(const String& path);
|
||||
|
||||
bool isRoot(InodeIdentifier) const;
|
||||
|
||||
bool touch(const String&path);
|
||||
|
||||
private:
|
||||
template<typename F> void enumerateDirectoryInode(InodeIdentifier, F func);
|
||||
InodeIdentifier resolvePath(const String& path);
|
||||
InodeIdentifier resolveSymbolicLink(const String& basePath, InodeIdentifier symlinkInode);
|
||||
|
||||
RetainPtr<Node> allocateNode();
|
||||
void freeNode(Node*);
|
||||
|
||||
RetainPtr<Node> makeNode(InodeIdentifier);
|
||||
RetainPtr<Node> getOrCreateNode(InodeIdentifier);
|
||||
|
||||
class Mount {
|
||||
public:
|
||||
Mount(InodeIdentifier host, RetainPtr<FileSystem>&&);
|
||||
|
||||
InodeIdentifier host() const { return m_host; }
|
||||
InodeIdentifier guest() const { return m_guest; }
|
||||
|
||||
private:
|
||||
InodeIdentifier m_host;
|
||||
InodeIdentifier m_guest;
|
||||
RetainPtr<FileSystem> m_fileSystem;
|
||||
};
|
||||
|
||||
Mount* findMountForHost(InodeIdentifier);
|
||||
Mount* findMountForGuest(InodeIdentifier);
|
||||
|
||||
HashMap<InodeIdentifier, Node*> m_inode2vnode;
|
||||
|
||||
Vector<OwnPtr<Mount>> m_mounts;
|
||||
|
||||
unsigned m_maxNodeCount { 0 };
|
||||
Node* m_nodes { nullptr };
|
||||
|
||||
Vector<Node*> m_nodeFreeList;
|
||||
|
||||
RetainPtr<Node> m_rootNode;
|
||||
};
|
||||
|
750
VirtualFileSystem/ext2_fs.h
Normal file
750
VirtualFileSystem/ext2_fs.h
Normal file
@ -0,0 +1,750 @@
|
||||
/*
|
||||
* linux/include/linux/ext2_fs.h
|
||||
*
|
||||
* Copyright (C) 1992, 1993, 1994, 1995
|
||||
* Remy Card (card@masi.ibp.fr)
|
||||
* Laboratoire MASI - Institut Blaise Pascal
|
||||
* Universite Pierre et Marie Curie (Paris VI)
|
||||
*
|
||||
* from
|
||||
*
|
||||
* linux/include/linux/minix_fs.h
|
||||
*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_EXT2_FS_H
|
||||
#define _LINUX_EXT2_FS_H
|
||||
|
||||
#include "ext2_types.h" /* Changed from linux/types.h */
|
||||
|
||||
/*
|
||||
* The second extended filesystem constants/structures
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define EXT2FS_DEBUG to produce debug messages
|
||||
*/
|
||||
#undef EXT2FS_DEBUG
|
||||
|
||||
/*
|
||||
* Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
|
||||
*/
|
||||
#define EXT2_PREALLOCATE
|
||||
#define EXT2_DEFAULT_PREALLOC_BLOCKS 8
|
||||
|
||||
/*
|
||||
* The second extended file system version
|
||||
*/
|
||||
#define EXT2FS_DATE "95/08/09"
|
||||
#define EXT2FS_VERSION "0.5b"
|
||||
|
||||
/*
|
||||
* Special inode numbers
|
||||
*/
|
||||
#define EXT2_BAD_INO 1 /* Bad blocks inode */
|
||||
#define EXT2_ROOT_INO 2 /* Root inode */
|
||||
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
|
||||
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
|
||||
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
|
||||
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
|
||||
#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
|
||||
#define EXT2_JOURNAL_INO 8 /* Journal inode */
|
||||
|
||||
/* First non-reserved inode for old ext2 filesystems */
|
||||
#define EXT2_GOOD_OLD_FIRST_INO 11
|
||||
|
||||
/*
|
||||
* The second extended file system magic number
|
||||
*/
|
||||
#define EXT2_SUPER_MAGIC 0xEF53
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define EXT2_SB(sb) (&((sb)->u.ext2_sb))
|
||||
#else
|
||||
/* Assume that user mode programs are passing in an ext2fs superblock, not
|
||||
* a kernel struct super_block. This will allow us to call the feature-test
|
||||
* macros from user land. */
|
||||
#define EXT2_SB(sb) (sb)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Maximal count of links to a file
|
||||
*/
|
||||
#define EXT2_LINK_MAX 65000
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage several block sizes
|
||||
*/
|
||||
#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
|
||||
#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
|
||||
#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
|
||||
#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
|
||||
#ifdef __KERNEL__
|
||||
#define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
|
||||
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
|
||||
#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits)
|
||||
#define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size)
|
||||
#define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino)
|
||||
#else
|
||||
#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
|
||||
#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
|
||||
#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
|
||||
EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
|
||||
#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
|
||||
EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
|
||||
#endif
|
||||
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage fragments
|
||||
*/
|
||||
#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
|
||||
#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
|
||||
#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
|
||||
#ifdef __KERNEL__
|
||||
# define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size)
|
||||
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block)
|
||||
#else
|
||||
# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
|
||||
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ACL structures
|
||||
*/
|
||||
struct ext2_acl_header /* Header of Access Control Lists */
|
||||
{
|
||||
__u32 aclh_size;
|
||||
__u32 aclh_file_count;
|
||||
__u32 aclh_acle_count;
|
||||
__u32 aclh_first_acle;
|
||||
};
|
||||
|
||||
struct ext2_acl_entry /* Access Control List Entry */
|
||||
{
|
||||
__u32 acle_size;
|
||||
__u16 acle_perms; /* Access permissions */
|
||||
__u16 acle_type; /* Type of entry */
|
||||
__u16 acle_tag; /* User or group identity */
|
||||
__u16 acle_pad1;
|
||||
__u32 acle_next; /* Pointer on next entry for the */
|
||||
/* same inode or on next free entry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure of a blocks group descriptor
|
||||
*/
|
||||
struct ext2_group_desc
|
||||
{
|
||||
__u32 bg_block_bitmap; /* Blocks bitmap block */
|
||||
__u32 bg_inode_bitmap; /* Inodes bitmap block */
|
||||
__u32 bg_inode_table; /* Inodes table block */
|
||||
__u16 bg_free_blocks_count; /* Free blocks count */
|
||||
__u16 bg_free_inodes_count; /* Free inodes count */
|
||||
__u16 bg_used_dirs_count; /* Directories count */
|
||||
__u16 bg_flags;
|
||||
__u32 bg_reserved[2];
|
||||
__u16 bg_itable_unused; /* Unused inodes count */
|
||||
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
|
||||
};
|
||||
|
||||
struct ext4_group_desc
|
||||
{
|
||||
__u32 bg_block_bitmap; /* Blocks bitmap block */
|
||||
__u32 bg_inode_bitmap; /* Inodes bitmap block */
|
||||
__u32 bg_inode_table; /* Inodes table block */
|
||||
__u16 bg_free_blocks_count; /* Free blocks count */
|
||||
__u16 bg_free_inodes_count; /* Free inodes count */
|
||||
__u16 bg_used_dirs_count; /* Directories count */
|
||||
__u16 bg_flags;
|
||||
__u32 bg_reserved[2];
|
||||
__u16 bg_itable_unused; /* Unused inodes count */
|
||||
__u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
|
||||
__u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */
|
||||
__u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */
|
||||
__u32 bg_inode_table_hi; /* Inodes table block MSB */
|
||||
__u16 bg_free_blocks_count_hi;/* Free blocks count MSB */
|
||||
__u16 bg_free_inodes_count_hi;/* Free inodes count MSB */
|
||||
__u16 bg_used_dirs_count_hi; /* Directories count MSB */
|
||||
__u16 bg_pad;
|
||||
__u32 bg_reserved2[3];
|
||||
};
|
||||
|
||||
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
|
||||
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
|
||||
#define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */
|
||||
|
||||
/*
|
||||
* Data structures used by the directory indexing feature
|
||||
*
|
||||
* Note: all of the multibyte integer fields are little endian.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note: dx_root_info is laid out so that if it should somehow get
|
||||
* overlaid by a dirent the two low bits of the hash version will be
|
||||
* zero. Therefore, the hash version mod 4 should never be 0.
|
||||
* Sincerely, the paranoia department.
|
||||
*/
|
||||
struct ext2_dx_root_info {
|
||||
__u32 reserved_zero;
|
||||
__u8 hash_version; /* 0 now, 1 at release */
|
||||
__u8 info_length; /* 8 */
|
||||
__u8 indirect_levels;
|
||||
__u8 unused_flags;
|
||||
};
|
||||
|
||||
#define EXT2_HASH_LEGACY 0
|
||||
#define EXT2_HASH_HALF_MD4 1
|
||||
#define EXT2_HASH_TEA 2
|
||||
#define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */
|
||||
#define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */
|
||||
#define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */
|
||||
|
||||
#define EXT2_HASH_FLAG_INCOMPAT 0x1
|
||||
|
||||
struct ext2_dx_entry {
|
||||
__u32 hash;
|
||||
__u32 block;
|
||||
};
|
||||
|
||||
struct ext2_dx_countlimit {
|
||||
__u16 limit;
|
||||
__u16 count;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Macro-instructions used to manage group descriptors
|
||||
*/
|
||||
#define EXT2_MIN_DESC_SIZE 32
|
||||
#define EXT2_MIN_DESC_SIZE_64BIT 64
|
||||
#define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE
|
||||
#define EXT2_DESC_SIZE(s) \
|
||||
((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \
|
||||
(s)->s_desc_size : EXT2_MIN_DESC_SIZE)
|
||||
|
||||
#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
|
||||
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
|
||||
#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
|
||||
/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
|
||||
#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
|
||||
#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
|
||||
#ifdef __KERNEL__
|
||||
#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
|
||||
#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
|
||||
#else
|
||||
#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Constants relative to the data blocks
|
||||
*/
|
||||
#define EXT2_NDIR_BLOCKS 12
|
||||
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
|
||||
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
|
||||
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
|
||||
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
|
||||
|
||||
/*
|
||||
* Inode flags
|
||||
*/
|
||||
#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
|
||||
#define EXT2_UNRM_FL 0x00000002 /* Undelete */
|
||||
#define EXT2_COMPR_FL 0x00000004 /* Compress file */
|
||||
#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
|
||||
#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
|
||||
#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
|
||||
#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
|
||||
#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
|
||||
/* Reserved for compression usage... */
|
||||
#define EXT2_DIRTY_FL 0x00000100
|
||||
#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
|
||||
#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
|
||||
#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
|
||||
/* End compression flags --- maybe not all used */
|
||||
#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
|
||||
#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
|
||||
#define EXT2_IMAGIC_FL 0x00002000
|
||||
#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
|
||||
#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
|
||||
#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
|
||||
#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
|
||||
#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
|
||||
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
|
||||
#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
|
||||
|
||||
#define EXT2_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
|
||||
#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
|
||||
|
||||
/*
|
||||
* ioctl commands
|
||||
*/
|
||||
|
||||
/* Used for online resize */
|
||||
struct ext2_new_group_input {
|
||||
__u32 group; /* Group number for this data */
|
||||
__u32 block_bitmap; /* Absolute block number of block bitmap */
|
||||
__u32 inode_bitmap; /* Absolute block number of inode bitmap */
|
||||
__u32 inode_table; /* Absolute block number of inode table start */
|
||||
__u32 blocks_count; /* Total number of blocks in this group */
|
||||
__u16 reserved_blocks; /* Number of reserved blocks in this group */
|
||||
__u16 unused; /* Number of reserved GDT blocks in group */
|
||||
};
|
||||
|
||||
struct ext4_new_group_input {
|
||||
__u32 group; /* Group number for this data */
|
||||
__u64 block_bitmap; /* Absolute block number of block bitmap */
|
||||
__u64 inode_bitmap; /* Absolute block number of inode bitmap */
|
||||
__u64 inode_table; /* Absolute block number of inode table start */
|
||||
__u32 blocks_count; /* Total number of blocks in this group */
|
||||
__u16 reserved_blocks; /* Number of reserved blocks in this group */
|
||||
__u16 unused;
|
||||
};
|
||||
|
||||
#ifdef __GNU__ /* Needed for the Hurd */
|
||||
#define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0)
|
||||
#endif
|
||||
|
||||
#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
|
||||
#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
|
||||
#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
|
||||
#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
|
||||
#define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long)
|
||||
#define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long)
|
||||
#define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
|
||||
#define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input)
|
||||
#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input)
|
||||
|
||||
/*
|
||||
* Structure of an inode on the disk
|
||||
*/
|
||||
struct ext2_inode {
|
||||
__u16 i_mode; /* File mode */
|
||||
__u16 i_uid; /* Low 16 bits of Owner Uid */
|
||||
__u32 i_size; /* Size in bytes */
|
||||
__u32 i_atime; /* Access time */
|
||||
__u32 i_ctime; /* Inode change time */
|
||||
__u32 i_mtime; /* Modification time */
|
||||
__u32 i_dtime; /* Deletion Time */
|
||||
__u16 i_gid; /* Low 16 bits of Group Id */
|
||||
__u16 i_links_count; /* Links count */
|
||||
__u32 i_blocks; /* Blocks count */
|
||||
__u32 i_flags; /* File flags */
|
||||
union {
|
||||
struct {
|
||||
__u32 l_i_version; /* was l_i_reserved1 */
|
||||
} linux1;
|
||||
struct {
|
||||
__u32 h_i_translator;
|
||||
} hurd1;
|
||||
} osd1; /* OS dependent 1 */
|
||||
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
|
||||
__u32 i_generation; /* File version (for NFS) */
|
||||
__u32 i_file_acl; /* File ACL */
|
||||
__u32 i_dir_acl; /* Directory ACL */
|
||||
__u32 i_faddr; /* Fragment address */
|
||||
union {
|
||||
struct {
|
||||
__u16 l_i_blocks_hi;
|
||||
__u16 l_i_file_acl_high;
|
||||
__u16 l_i_uid_high; /* these 2 fields */
|
||||
__u16 l_i_gid_high; /* were reserved2[0] */
|
||||
__u32 l_i_reserved2;
|
||||
} linux2;
|
||||
struct {
|
||||
__u8 h_i_frag; /* Fragment number */
|
||||
__u8 h_i_fsize; /* Fragment size */
|
||||
__u16 h_i_mode_high;
|
||||
__u16 h_i_uid_high;
|
||||
__u16 h_i_gid_high;
|
||||
__u32 h_i_author;
|
||||
} hurd2;
|
||||
} osd2; /* OS dependent 2 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Permanent part of an large inode on the disk
|
||||
*/
|
||||
struct ext2_inode_large {
|
||||
__u16 i_mode; /* File mode */
|
||||
__u16 i_uid; /* Low 16 bits of Owner Uid */
|
||||
__u32 i_size; /* Size in bytes */
|
||||
__u32 i_atime; /* Access time */
|
||||
__u32 i_ctime; /* Inode Change time */
|
||||
__u32 i_mtime; /* Modification time */
|
||||
__u32 i_dtime; /* Deletion Time */
|
||||
__u16 i_gid; /* Low 16 bits of Group Id */
|
||||
__u16 i_links_count; /* Links count */
|
||||
__u32 i_blocks; /* Blocks count */
|
||||
__u32 i_flags; /* File flags */
|
||||
union {
|
||||
struct {
|
||||
__u32 l_i_version; /* was l_i_reserved1 */
|
||||
} linux1;
|
||||
struct {
|
||||
__u32 h_i_translator;
|
||||
} hurd1;
|
||||
} osd1; /* OS dependent 1 */
|
||||
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
|
||||
__u32 i_generation; /* File version (for NFS) */
|
||||
__u32 i_file_acl; /* File ACL */
|
||||
__u32 i_dir_acl; /* Directory ACL */
|
||||
__u32 i_faddr; /* Fragment address */
|
||||
union {
|
||||
struct {
|
||||
__u16 l_i_blocks_hi;
|
||||
__u16 l_i_file_acl_high;
|
||||
__u16 l_i_uid_high; /* these 2 fields */
|
||||
__u16 l_i_gid_high; /* were reserved2[0] */
|
||||
__u32 l_i_reserved2;
|
||||
} linux2;
|
||||
struct {
|
||||
__u8 h_i_frag; /* Fragment number */
|
||||
__u8 h_i_fsize; /* Fragment size */
|
||||
__u16 h_i_mode_high;
|
||||
__u16 h_i_uid_high;
|
||||
__u16 h_i_gid_high;
|
||||
__u32 h_i_author;
|
||||
} hurd2;
|
||||
} osd2; /* OS dependent 2 */
|
||||
__u16 i_extra_isize;
|
||||
__u16 i_pad1;
|
||||
__u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
|
||||
__u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */
|
||||
__u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
|
||||
__u32 i_crtime; /* File creation time */
|
||||
__u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
|
||||
__u32 i_version_hi; /* high 32 bits for 64-bit version */
|
||||
};
|
||||
|
||||
#define i_size_high i_dir_acl
|
||||
|
||||
#if defined(__KERNEL__) || defined(__linux__)
|
||||
#define i_reserved1 osd1.linux1.l_i_reserved1
|
||||
#define i_frag osd2.linux2.l_i_frag
|
||||
#define i_fsize osd2.linux2.l_i_fsize
|
||||
#define i_uid_low i_uid
|
||||
#define i_gid_low i_gid
|
||||
#define i_uid_high osd2.linux2.l_i_uid_high
|
||||
#define i_gid_high osd2.linux2.l_i_gid_high
|
||||
#define i_reserved2 osd2.linux2.l_i_reserved2
|
||||
#else
|
||||
#if defined(__GNU__)
|
||||
|
||||
#define i_translator osd1.hurd1.h_i_translator
|
||||
#define i_frag osd2.hurd2.h_i_frag;
|
||||
#define i_fsize osd2.hurd2.h_i_fsize;
|
||||
#define i_uid_high osd2.hurd2.h_i_uid_high
|
||||
#define i_gid_high osd2.hurd2.h_i_gid_high
|
||||
#define i_author osd2.hurd2.h_i_author
|
||||
|
||||
#endif /* __GNU__ */
|
||||
#endif /* defined(__KERNEL__) || defined(__linux__) */
|
||||
|
||||
#define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16)
|
||||
#define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16)
|
||||
#define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x))
|
||||
#define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x))
|
||||
|
||||
/*
|
||||
* File system states
|
||||
*/
|
||||
#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
|
||||
#define EXT2_ERROR_FS 0x0002 /* Errors detected */
|
||||
#define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */
|
||||
|
||||
/*
|
||||
* Misc. filesystem flags
|
||||
*/
|
||||
#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */
|
||||
#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */
|
||||
#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */
|
||||
|
||||
/*
|
||||
* Mount flags
|
||||
*/
|
||||
#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
|
||||
#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
|
||||
#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
|
||||
#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
|
||||
#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
|
||||
#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
|
||||
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
|
||||
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
|
||||
|
||||
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
|
||||
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
|
||||
#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
|
||||
EXT2_MOUNT_##opt)
|
||||
/*
|
||||
* Maximal mount counts between two filesystem checks
|
||||
*/
|
||||
#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
|
||||
#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
|
||||
|
||||
/*
|
||||
* Behaviour when detecting errors
|
||||
*/
|
||||
#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
|
||||
#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
|
||||
#define EXT2_ERRORS_PANIC 3 /* Panic */
|
||||
#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
|
||||
|
||||
/*
|
||||
* Structure of the super block
|
||||
*/
|
||||
struct ext2_super_block {
|
||||
__u32 s_inodes_count; /* Inodes count */
|
||||
__u32 s_blocks_count; /* Blocks count */
|
||||
__u32 s_r_blocks_count; /* Reserved blocks count */
|
||||
__u32 s_free_blocks_count; /* Free blocks count */
|
||||
__u32 s_free_inodes_count; /* Free inodes count */
|
||||
__u32 s_first_data_block; /* First Data Block */
|
||||
__u32 s_log_block_size; /* Block size */
|
||||
__s32 s_log_frag_size; /* Fragment size */
|
||||
__u32 s_blocks_per_group; /* # Blocks per group */
|
||||
__u32 s_frags_per_group; /* # Fragments per group */
|
||||
__u32 s_inodes_per_group; /* # Inodes per group */
|
||||
__u32 s_mtime; /* Mount time */
|
||||
__u32 s_wtime; /* Write time */
|
||||
__u16 s_mnt_count; /* Mount count */
|
||||
__s16 s_max_mnt_count; /* Maximal mount count */
|
||||
__u16 s_magic; /* Magic signature */
|
||||
__u16 s_state; /* File system state */
|
||||
__u16 s_errors; /* Behaviour when detecting errors */
|
||||
__u16 s_minor_rev_level; /* minor revision level */
|
||||
__u32 s_lastcheck; /* time of last check */
|
||||
__u32 s_checkinterval; /* max. time between checks */
|
||||
__u32 s_creator_os; /* OS */
|
||||
__u32 s_rev_level; /* Revision level */
|
||||
__u16 s_def_resuid; /* Default uid for reserved blocks */
|
||||
__u16 s_def_resgid; /* Default gid for reserved blocks */
|
||||
/*
|
||||
* These fields are for EXT2_DYNAMIC_REV superblocks only.
|
||||
*
|
||||
* Note: the difference between the compatible feature set and
|
||||
* the incompatible feature set is that if there is a bit set
|
||||
* in the incompatible feature set that the kernel doesn't
|
||||
* know about, it should refuse to mount the filesystem.
|
||||
*
|
||||
* e2fsck's requirements are more strict; if it doesn't know
|
||||
* about a feature in either the compatible or incompatible
|
||||
* feature set, it must abort and not try to meddle with
|
||||
* things it doesn't understand...
|
||||
*/
|
||||
__u32 s_first_ino; /* First non-reserved inode */
|
||||
__u16 s_inode_size; /* size of inode structure */
|
||||
__u16 s_block_group_nr; /* block group # of this superblock */
|
||||
__u32 s_feature_compat; /* compatible feature set */
|
||||
__u32 s_feature_incompat; /* incompatible feature set */
|
||||
__u32 s_feature_ro_compat; /* readonly-compatible feature set */
|
||||
__u8 s_uuid[16]; /* 128-bit uuid for volume */
|
||||
char s_volume_name[16]; /* volume name */
|
||||
char s_last_mounted[64]; /* directory where last mounted */
|
||||
__u32 s_algorithm_usage_bitmap; /* For compression */
|
||||
/*
|
||||
* Performance hints. Directory preallocation should only
|
||||
* happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
|
||||
*/
|
||||
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
|
||||
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
|
||||
__u16 s_reserved_gdt_blocks; /* Per group table for online growth */
|
||||
/*
|
||||
* Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
|
||||
*/
|
||||
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
|
||||
__u32 s_journal_inum; /* inode number of journal file */
|
||||
__u32 s_journal_dev; /* device number of journal file */
|
||||
__u32 s_last_orphan; /* start of list of inodes to delete */
|
||||
__u32 s_hash_seed[4]; /* HTREE hash seed */
|
||||
__u8 s_def_hash_version; /* Default hash version to use */
|
||||
__u8 s_jnl_backup_type; /* Default type of journal backup */
|
||||
__u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */
|
||||
__u32 s_default_mount_opts;
|
||||
__u32 s_first_meta_bg; /* First metablock group */
|
||||
__u32 s_mkfs_time; /* When the filesystem was created */
|
||||
__u32 s_jnl_blocks[17]; /* Backup of the journal inode */
|
||||
__u32 s_blocks_count_hi; /* Blocks count high 32bits */
|
||||
__u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/
|
||||
__u32 s_free_blocks_hi; /* Free blocks count */
|
||||
__u16 s_min_extra_isize; /* All inodes have at least # bytes */
|
||||
__u16 s_want_extra_isize; /* New inodes should reserve # bytes */
|
||||
__u32 s_flags; /* Miscellaneous flags */
|
||||
__u16 s_raid_stride; /* RAID stride */
|
||||
__u16 s_mmp_interval; /* # seconds to wait in MMP checking */
|
||||
__u64 s_mmp_block; /* Block for multi-mount protection */
|
||||
__u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
|
||||
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
|
||||
__u8 s_reserved_char_pad;
|
||||
__u16 s_reserved_pad; /* Padding to next 32bits */
|
||||
__u32 s_reserved[162]; /* Padding to the end of the block */
|
||||
};
|
||||
|
||||
/*
|
||||
* Codes for operating systems
|
||||
*/
|
||||
#define EXT2_OS_LINUX 0
|
||||
#define EXT2_OS_HURD 1
|
||||
#define EXT2_OBSO_OS_MASIX 2
|
||||
#define EXT2_OS_FREEBSD 3
|
||||
#define EXT2_OS_LITES 4
|
||||
|
||||
/*
|
||||
* Revision levels
|
||||
*/
|
||||
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
|
||||
#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
|
||||
|
||||
#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
|
||||
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
|
||||
|
||||
#define EXT2_GOOD_OLD_INODE_SIZE 128
|
||||
|
||||
/*
|
||||
* Journal inode backup types
|
||||
*/
|
||||
#define EXT3_JNL_BACKUP_BLOCKS 1
|
||||
|
||||
/*
|
||||
* Feature set definitions
|
||||
*/
|
||||
|
||||
#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_feature_compat & (mask) )
|
||||
#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
|
||||
#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
|
||||
( EXT2_SB(sb)->s_feature_incompat & (mask) )
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
|
||||
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
|
||||
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
|
||||
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
|
||||
#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
|
||||
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
|
||||
#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040
|
||||
|
||||
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
|
||||
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
|
||||
/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
|
||||
#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008
|
||||
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
|
||||
#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020
|
||||
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
|
||||
|
||||
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
|
||||
#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
|
||||
#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
|
||||
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
|
||||
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
|
||||
#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
|
||||
#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
|
||||
#define EXT4_FEATURE_INCOMPAT_MMP 0x0100
|
||||
#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200
|
||||
|
||||
|
||||
#define EXT2_FEATURE_COMPAT_SUPP 0
|
||||
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
|
||||
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
|
||||
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
|
||||
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
|
||||
EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
|
||||
|
||||
/*
|
||||
* Default values for user and/or group using reserved blocks
|
||||
*/
|
||||
#define EXT2_DEF_RESUID 0
|
||||
#define EXT2_DEF_RESGID 0
|
||||
|
||||
/*
|
||||
* Default mount options
|
||||
*/
|
||||
#define EXT2_DEFM_DEBUG 0x0001
|
||||
#define EXT2_DEFM_BSDGROUPS 0x0002
|
||||
#define EXT2_DEFM_XATTR_USER 0x0004
|
||||
#define EXT2_DEFM_ACL 0x0008
|
||||
#define EXT2_DEFM_UID16 0x0010
|
||||
#define EXT3_DEFM_JMODE 0x0060
|
||||
#define EXT3_DEFM_JMODE_DATA 0x0020
|
||||
#define EXT3_DEFM_JMODE_ORDERED 0x0040
|
||||
#define EXT3_DEFM_JMODE_WBACK 0x0060
|
||||
|
||||
/*
|
||||
* Structure of a directory entry
|
||||
*/
|
||||
#define EXT2_NAME_LEN 255
|
||||
|
||||
struct ext2_dir_entry {
|
||||
__u32 inode; /* Inode number */
|
||||
__u16 rec_len; /* Directory entry length */
|
||||
__u16 name_len; /* Name length */
|
||||
char name[EXT2_NAME_LEN]; /* File name */
|
||||
};
|
||||
|
||||
/*
|
||||
* The new version of the directory entry. Since EXT2 structures are
|
||||
* stored in intel byte order, and the name_len field could never be
|
||||
* bigger than 255 chars, it's safe to reclaim the extra byte for the
|
||||
* file_type field.
|
||||
*/
|
||||
struct ext2_dir_entry_2 {
|
||||
__u32 inode; /* Inode number */
|
||||
__u16 rec_len; /* Directory entry length */
|
||||
__u8 name_len; /* Name length */
|
||||
__u8 file_type;
|
||||
char name[EXT2_NAME_LEN]; /* File name */
|
||||
};
|
||||
|
||||
/*
|
||||
* Ext2 directory file types. Only the low 3 bits are used. The
|
||||
* other bits are reserved for now.
|
||||
*/
|
||||
#define EXT2_FT_UNKNOWN 0
|
||||
#define EXT2_FT_REG_FILE 1
|
||||
#define EXT2_FT_DIR 2
|
||||
#define EXT2_FT_CHRDEV 3
|
||||
#define EXT2_FT_BLKDEV 4
|
||||
#define EXT2_FT_FIFO 5
|
||||
#define EXT2_FT_SOCK 6
|
||||
#define EXT2_FT_SYMLINK 7
|
||||
|
||||
#define EXT2_FT_MAX 8
|
||||
|
||||
/*
|
||||
* EXT2_DIR_PAD defines the directory entries boundaries
|
||||
*
|
||||
* NOTE: It must be a multiple of 4
|
||||
*/
|
||||
#define EXT2_DIR_PAD 4
|
||||
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
|
||||
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
|
||||
~EXT2_DIR_ROUND)
|
||||
|
||||
/*
|
||||
* This structure will be used for multiple mount protection. It will be
|
||||
* written into the block number saved in the s_mmp_block field in the
|
||||
* superblock.
|
||||
*/
|
||||
#define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */
|
||||
#define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */
|
||||
#define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */
|
||||
|
||||
struct mmp_struct {
|
||||
__u32 mmp_magic;
|
||||
__u32 mmp_seq;
|
||||
__u64 mmp_time;
|
||||
char mmp_nodename[64];
|
||||
char mmp_bdevname[32];
|
||||
__u16 mmp_interval;
|
||||
__u16 mmp_pad1;
|
||||
__u32 mmp_pad2;
|
||||
};
|
||||
|
||||
/*
|
||||
* Interval in number of seconds to update the MMP sequence number.
|
||||
*/
|
||||
#define EXT2_MMP_DEF_INTERVAL 5
|
||||
|
||||
#endif /* _LINUX_EXT2_FS_H */
|
145
VirtualFileSystem/ext2_types.h
Normal file
145
VirtualFileSystem/ext2_types.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* If linux/types.h is already been included, assume it has defined
|
||||
* everything we need. (cross fingers) Other header files may have
|
||||
* also defined the types that we need.
|
||||
*/
|
||||
#if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \
|
||||
!defined(_EXT2_TYPES_H))
|
||||
#define _EXT2_TYPES_H
|
||||
|
||||
#define __S8_TYPEDEF __signed__ char
|
||||
#define __U8_TYPEDEF unsigned char
|
||||
#define __S16_TYPEDEF __signed__ short
|
||||
#define __U16_TYPEDEF unsigned short
|
||||
#define __S32_TYPEDEF __signed__ int
|
||||
#define __U32_TYPEDEF unsigned int
|
||||
#define __S64_TYPEDEF __signed__ long long
|
||||
#define __U64_TYPEDEF unsigned long long
|
||||
|
||||
#ifdef __U8_TYPEDEF
|
||||
typedef __U8_TYPEDEF __u8;
|
||||
#else
|
||||
typedef unsigned char __u8;
|
||||
#endif
|
||||
|
||||
#ifdef __S8_TYPEDEF
|
||||
typedef __S8_TYPEDEF __s8;
|
||||
#else
|
||||
typedef signed char __s8;
|
||||
#endif
|
||||
|
||||
#ifdef __U16_TYPEDEF
|
||||
typedef __U16_TYPEDEF __u16;
|
||||
#else
|
||||
#if (4 == 2)
|
||||
typedef unsigned int __u16;
|
||||
#else
|
||||
#if (2 == 2)
|
||||
typedef unsigned short __u16;
|
||||
#else
|
||||
?==error: undefined 16 bit type
|
||||
#endif /* SIZEOF_SHORT == 2 */
|
||||
#endif /* SIZEOF_INT == 2 */
|
||||
#endif /* __U16_TYPEDEF */
|
||||
|
||||
#ifdef __S16_TYPEDEF
|
||||
typedef __S16_TYPEDEF __s16;
|
||||
#else
|
||||
#if (4 == 2)
|
||||
typedef int __s16;
|
||||
#else
|
||||
#if (2 == 2)
|
||||
typedef short __s16;
|
||||
#else
|
||||
?==error: undefined 16 bit type
|
||||
#endif /* SIZEOF_SHORT == 2 */
|
||||
#endif /* SIZEOF_INT == 2 */
|
||||
#endif /* __S16_TYPEDEF */
|
||||
|
||||
|
||||
#ifdef __U32_TYPEDEF
|
||||
typedef __U32_TYPEDEF __u32;
|
||||
#else
|
||||
#if (4 == 4)
|
||||
typedef unsigned int __u32;
|
||||
#else
|
||||
#if (4 == 4)
|
||||
typedef unsigned long __u32;
|
||||
#else
|
||||
#if (2 == 4)
|
||||
typedef unsigned short __u32;
|
||||
#else
|
||||
?== error: undefined 32 bit type
|
||||
#endif /* SIZEOF_SHORT == 4 */
|
||||
#endif /* SIZEOF_LONG == 4 */
|
||||
#endif /* SIZEOF_INT == 4 */
|
||||
#endif /* __U32_TYPEDEF */
|
||||
|
||||
#ifdef __S32_TYPEDEF
|
||||
typedef __S32_TYPEDEF __s32;
|
||||
#else
|
||||
#if (4 == 4)
|
||||
typedef int __s32;
|
||||
#else
|
||||
#if (4 == 4)
|
||||
typedef long __s32;
|
||||
#else
|
||||
#if (2 == 4)
|
||||
typedef short __s32;
|
||||
#else
|
||||
?== error: undefined 32 bit type
|
||||
#endif /* SIZEOF_SHORT == 4 */
|
||||
#endif /* SIZEOF_LONG == 4 */
|
||||
#endif /* SIZEOF_INT == 4 */
|
||||
#endif /* __S32_TYPEDEF */
|
||||
|
||||
#ifdef __U64_TYPEDEF
|
||||
typedef __U64_TYPEDEF __u64;
|
||||
#else
|
||||
#if (4 == 8)
|
||||
typedef unsigned int __u64;
|
||||
#else
|
||||
#if (4 == 8)
|
||||
typedef unsigned long __u64;
|
||||
#else
|
||||
#if (8 == 8)
|
||||
typedef unsigned long long __u64;
|
||||
#endif /* SIZEOF_LONG_LONG == 8 */
|
||||
#endif /* SIZEOF_LONG == 8 */
|
||||
#endif /* SIZEOF_INT == 8 */
|
||||
#endif /* __U64_TYPEDEF */
|
||||
|
||||
#ifdef __S64_TYPEDEF
|
||||
typedef __S64_TYPEDEF __s64;
|
||||
#else
|
||||
#if (4 == 8)
|
||||
typedef int __s64;
|
||||
#else
|
||||
#if (4 == 8)
|
||||
typedef long __s64;
|
||||
#else
|
||||
#if (8 == 8)
|
||||
#if defined(__GNUC__)
|
||||
typedef __signed__ long long __s64;
|
||||
#else
|
||||
typedef signed long long __s64;
|
||||
#endif /* __GNUC__ */
|
||||
#endif /* SIZEOF_LONG_LONG == 8 */
|
||||
#endif /* SIZEOF_LONG == 8 */
|
||||
#endif /* SIZEOF_INT == 8 */
|
||||
#endif /* __S64_TYPEDEF */
|
||||
|
||||
#undef __S8_TYPEDEF
|
||||
#undef __U8_TYPEDEF
|
||||
#undef __S16_TYPEDEF
|
||||
#undef __U16_TYPEDEF
|
||||
#undef __S32_TYPEDEF
|
||||
#undef __U32_TYPEDEF
|
||||
#undef __S64_TYPEDEF
|
||||
#undef __U64_TYPEDEF
|
||||
|
||||
#endif /* _*_TYPES_H */
|
||||
|
||||
/* These defines are needed for the public ext2fs.h header file */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#undef WORDS_BIGENDIAN
|
BIN
VirtualFileSystem/small.fs
Normal file
BIN
VirtualFileSystem/small.fs
Normal file
Binary file not shown.
158
VirtualFileSystem/test.cpp
Normal file
158
VirtualFileSystem/test.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
#include "Ext2FileSystem.h"
|
||||
#include "FileBackedBlockDevice.h"
|
||||
#include "VirtualFileSystem.h"
|
||||
#include "FileHandle.h"
|
||||
#include "SyntheticFileSystem.h"
|
||||
#include <cstring>
|
||||
#include <AK/SimpleMalloc.h>
|
||||
#include <AK/kmalloc.h>
|
||||
|
||||
static RetainPtr<FileSystem> makeFileSystem(const char* imagePath);
|
||||
|
||||
int main(int c, char** v)
|
||||
{
|
||||
const char* filename = "small.fs";
|
||||
if (c >= 2)
|
||||
filename = v[1];
|
||||
|
||||
VirtualFileSystem vfs;
|
||||
|
||||
if (!vfs.mountRoot(makeFileSystem(filename))) {
|
||||
printf("Failed to mount root :(\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto newFile = vfs.create("/empty");
|
||||
printf("vfs.create: %p\n", newFile.ptr());
|
||||
//return 0;
|
||||
|
||||
if (!strcmp(v[0], "./vcat")) {
|
||||
auto handle = vfs.open(v[2]);
|
||||
if (!handle) {
|
||||
printf("failed to open %s inside fs image\n", v[2]);
|
||||
return 1;
|
||||
}
|
||||
auto contents = handle->read();
|
||||
|
||||
FILE* fout = fopen(v[3], "w");
|
||||
if (!fout) {
|
||||
printf("failed to open %s for output\n", v[3]);
|
||||
return 1;
|
||||
}
|
||||
fwrite(contents.pointer(), sizeof(char), contents.size(), fout);
|
||||
fclose(fout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto synthfs = SyntheticFileSystem::create();
|
||||
bool success = static_cast<FileSystem&>(*synthfs).initialize();
|
||||
printf("synth->initialize(): returned %u\n", success);
|
||||
|
||||
vfs.mount(std::move(synthfs), "/syn");
|
||||
|
||||
vfs.listDirectory("/");
|
||||
printf("list /syn:\n");
|
||||
vfs.listDirectory("/syn");
|
||||
|
||||
#if 0
|
||||
auto handle = vfs.open("/home/andreas/../../home/./andreas/./file2");
|
||||
printf("handle = %p\n", handle.ptr());
|
||||
ASSERT(handle);
|
||||
|
||||
auto contents = handle->read();
|
||||
ASSERT(contents);
|
||||
|
||||
printf("contents: '%s'\n", contents->pointer());
|
||||
#endif
|
||||
|
||||
String currentDirectory = "/";
|
||||
|
||||
while (true) {
|
||||
char cmdbuf[256];
|
||||
printf("::>");
|
||||
fflush(stdout);
|
||||
fgets(cmdbuf, sizeof(cmdbuf), stdin);
|
||||
|
||||
if (cmdbuf[strlen(cmdbuf) - 1] == '\n')
|
||||
cmdbuf[strlen(cmdbuf) - 1] = '\0';
|
||||
|
||||
String command = cmdbuf;
|
||||
auto parts = command.split(' ');
|
||||
|
||||
if (parts.isEmpty())
|
||||
continue;
|
||||
|
||||
String cmd = parts[0];
|
||||
|
||||
if (cmd == "q")
|
||||
break;
|
||||
|
||||
if (cmd == "pwd") {
|
||||
printf("%s\n", currentDirectory.characters());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == "ls") {
|
||||
vfs.listDirectory(currentDirectory);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == "lr") {
|
||||
vfs.listDirectoryRecursively(currentDirectory);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == "cd" && parts.size() > 1) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%s/%s", currentDirectory.characters(), parts[1].characters());
|
||||
if (vfs.isDirectory(buf)) {
|
||||
currentDirectory = buf;
|
||||
} else {
|
||||
printf("No such directory: %s\n", buf);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == "mt" && parts.size() > 1) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%s/%s", currentDirectory.characters(), parts[1].characters());
|
||||
vfs.touch(buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (cmd == "cat" && parts.size() > 1) {
|
||||
char pathbuf[1024];
|
||||
sprintf(pathbuf, "%s/%s", currentDirectory.characters(), parts[1].characters());
|
||||
auto handle = vfs.open(pathbuf);
|
||||
if (!handle) {
|
||||
printf("failed to open %s\n", pathbuf);
|
||||
continue;
|
||||
}
|
||||
auto contents = handle->read();
|
||||
fwrite(contents.pointer(), sizeof(char), contents.size(), stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd == "ma") {
|
||||
SimpleMalloc::dump();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RetainPtr<FileSystem> makeFileSystem(const char* imagePath)
|
||||
{
|
||||
auto fsImage = FileBackedBlockDevice::create(imagePath, 512);
|
||||
if (!fsImage->isValid()) {
|
||||
fprintf(stderr, "Failed to open fs image file '%s'\n", imagePath);
|
||||
exit(1);
|
||||
}
|
||||
auto ext2 = Ext2FileSystem::create(std::move(fsImage));
|
||||
|
||||
bool success = static_cast<FileSystem&>(*ext2).initialize();
|
||||
printf("ext2->initialize(): returned %u\n", success);
|
||||
return ext2;
|
||||
}
|
Loading…
Reference in New Issue
Block a user