Compare commits

..

5 Commits

Author SHA1 Message Date
6c3b7672a0
Kernel: Demo the initrd using TarStream
All checks were successful
continuous-integration/drone/push Build is passing
Yes, we're using the physical address. Not optimal, this is only for demo purposes.
2022-12-18 16:39:35 +01:00
1d6092341a
Add a generic TarStream class
As long as it's in memory, we can use it :)
2022-12-18 16:38:47 +01:00
eadca3d25b
Add nullcpy()
Invented function to memcpy() with a specific size, but add a null terminator.
2022-12-18 16:31:02 +01:00
0d65f188f0
Alignment.h: Include the Types 2022-12-18 16:30:27 +01:00
730d0682ee
Result: Add try_set_value_with_specific_error() 2022-12-18 16:30:09 +01:00
8 changed files with 193 additions and 0 deletions

View File

@ -4,14 +4,18 @@
#include "arch/Serial.h" #include "arch/Serial.h"
#include "arch/Timer.h" #include "arch/Timer.h"
#include "boot/Init.h" #include "boot/Init.h"
#include "boot/bootboot.h"
#include "config.h" #include "config.h"
#include "memory/Heap.h" #include "memory/Heap.h"
#include "memory/KernelVM.h" #include "memory/KernelVM.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/TarStream.h>
#include <luna/Units.h> #include <luna/Units.h>
extern const BOOTBOOT bootboot;
void async_thread() void async_thread()
{ {
while (true) while (true)
@ -49,6 +53,15 @@ Result<void> init()
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars()); kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars()); kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
TarStream stream((void*)bootboot.initrd_ptr, bootboot.initrd_size);
TarStream::Entry entry;
while (TRY(stream.read_next_entry().try_set_value_with_specific_error(entry, 0)))
{
if (entry.type == TarStream::EntryType::RegularFile)
kinfoln("Found file %s in initial ramdisk, of size %s", entry.name,
to_dynamic_unit(entry.size).release_value().chars());
}
Thread::init(); Thread::init();
Scheduler::init(); Scheduler::init();

View File

@ -11,6 +11,7 @@ set(FREESTANDING_SOURCES
src/Alloc.cpp src/Alloc.cpp
src/OwnedStringView.cpp src/OwnedStringView.cpp
src/Utf8.cpp src/Utf8.cpp
src/TarStream.cpp
) )
set(SOURCES set(SOURCES

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <luna/TypeTraits.h> #include <luna/TypeTraits.h>
#include <luna/Types.h>
// Must ALWAYS be called with a power of two as alignment. // Must ALWAYS be called with a power of two as alignment.
template <usize alignment, typename T> constexpr T is_aligned(T value) template <usize alignment, typename T> constexpr T is_aligned(T value)

View File

@ -12,4 +12,7 @@ extern "C"
usize wcslen(const wchar_t* str); usize wcslen(const wchar_t* str);
char* strdup(const char* str); char* strdup(const char* str);
// Copies len bytes from src into dest and adds a null terminator.
void nullcpy(char* dest, const char* src, usize len);
} }

View File

@ -90,6 +90,13 @@ template <typename T> class Result
return m_value.try_set_value(ref); return m_value.try_set_value(ref);
} }
Result<bool> try_set_value_with_specific_error(T& ref, int error)
{
if (has_error() && m_error != error) return release_error();
return m_value.try_set_value(ref);
}
T release_value() T release_value()
{ {
expect(has_value(), "Result::release_value() called on a Result that holds an error"); expect(has_value(), "Result::release_value() called on a Result that holds an error");

View File

@ -0,0 +1,66 @@
#pragma once
#include <luna/Result.h>
#include <luna/Types.h>
class TarStream
{
public:
enum class EntryType
{
RegularFile,
Directory
};
struct Entry
{
char name[257];
usize size;
EntryType type;
private:
usize pos;
friend class TarStream;
};
TarStream(void* base, usize size);
void initialize(void* base, usize size);
Result<Entry> read_next_entry();
void rewind();
usize read_contents(const Entry& entry, char* buf, usize offset, usize length);
private:
struct [[gnu::packed]] TarHeader
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
};
Result<void> find_valid_header(TarHeader* out, bool& success);
Result<void> read_header(TarHeader* out);
Result<Entry> parse_header(const TarHeader* hdr);
void* m_base;
void* m_pos;
usize m_size;
usize m_offset = 0;
};

View File

@ -65,4 +65,10 @@ extern "C"
return dest; return dest;
} }
void nullcpy(char* dest, const char* src, usize len)
{
memcpy(dest, src, len);
dest[len] = 0;
}
} }

96
luna/src/TarStream.cpp Normal file
View File

@ -0,0 +1,96 @@
#include <luna/Alignment.h>
#include <luna/CString.h>
#include <luna/NumberParsing.h>
#include <luna/TarStream.h>
TarStream::TarStream(void* base, usize size) : m_base(base), m_pos(base), m_size(size)
{
}
void TarStream::initialize(void* base, usize size)
{
m_base = m_pos = base;
m_size = size;
m_offset = 0;
}
void TarStream::rewind()
{
m_pos = m_base;
m_offset = 0;
}
Result<void> TarStream::read_header(TarHeader* out)
{
if ((m_offset + 512) > m_size) return err(0);
memcpy(out, m_pos, sizeof(TarHeader));
m_pos = offset_ptr(m_pos, 512);
m_offset += 512;
return {};
}
Result<TarStream::Entry> TarStream::parse_header(const TarStream::TarHeader* hdr)
{
Entry entry;
char size[13];
nullcpy(size, hdr->size, 12);
entry.size = parse_unsigned_integer(size, nullptr, 8);
entry.pos = m_offset;
switch (hdr->typeflag)
{
case '\0':
case '0': entry.type = EntryType::RegularFile; break;
case '5': entry.type = EntryType::Directory; break;
default: return err(EFIXME);
}
if (!strlen(hdr->prefix)) { nullcpy(entry.name, hdr->name, 100); }
else { return err(EFIXME); }
if (entry.size)
{
m_pos = offset_ptr(m_pos, align_up<512ul>(entry.size));
m_offset += align_up<512ul>(entry.size);
}
return entry;
}
Result<void> TarStream::find_valid_header(TarStream::TarHeader* out, bool& success)
{
success = false;
while (true)
{
TRY(read_header(out));
if (!memcmp(out->magic, "ustar", 5))
{
success = true;
return {};
}
}
}
// FIXME: How do we know whether an error is an error or just EOF? Returning an error code of 0 as EOF seems a bit
// clunky to me...
Result<TarStream::Entry> TarStream::read_next_entry()
{
bool success;
TarHeader header;
auto rc = find_valid_header(&header, success);
if (!success) return rc.release_error();
return parse_header(&header);
}
usize TarStream::read_contents(const Entry& entry, char* buf, usize offset, usize length)
{
if (offset >= entry.size) return 0;
if ((length + offset) > entry.size) length = entry.size - offset;
memcpy(buf, offset_ptr(m_base, entry.pos + offset), length);
return length;
}