Add a generic TarStream class
As long as it's in memory, we can use it :)
This commit is contained in:
parent
eadca3d25b
commit
1d6092341a
@ -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
|
||||||
|
66
luna/include/luna/TarStream.h
Normal file
66
luna/include/luna/TarStream.h
Normal 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;
|
||||||
|
};
|
96
luna/src/TarStream.cpp
Normal file
96
luna/src/TarStream.cpp
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user