diff --git a/kernel/initrd.cpp b/kernel/initrd.cpp
index 08f98c2d..49e55b3c 100644
--- a/kernel/initrd.cpp
+++ b/kernel/initrd.cpp
@@ -18,7 +18,7 @@
Sortix. If not, see .
initrd.cpp
- Provides low-level access to a Sortix init ramdisk.
+ Extracts the initrd into the initial memory filesystem.
*******************************************************************************/
@@ -53,239 +53,137 @@
namespace Sortix {
namespace InitRD {
-addralloc_t initrd_addr_alloc;
-uint8_t* initrd = NULL;
-size_t initrdsize;
-const initrd_superblock_t* sb;
+// TODO: The initrd is not being properly verified.
+// TODO: The initrd is not handled in an endian-neutral manner.
-__attribute__((unused))
-static uint32_t HostModeToInitRD(mode_t mode)
+struct initrd_context
{
- uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
- if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; }
- if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; }
- if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; }
- if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; }
- if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; }
- if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; }
- if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; }
- if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; }
+ uint8_t* initrd;
+ size_t initrd_size;
+ initrd_superblock_t* sb;
+ Ref links;
+ ioctx_t ioctx;
+};
+
+static mode_t initrd_mode_to_host_mode(uint32_t mode)
+{
+ mode_t result = mode & 0777;
+ if ( INITRD_S_ISVTX & mode ) result |= S_ISVTX;
+ if ( INITRD_S_ISSOCK(mode) ) result |= S_IFSOCK;
+ if ( INITRD_S_ISLNK(mode) ) result |= S_IFLNK;
+ if ( INITRD_S_ISREG(mode) ) result |= S_IFREG;
+ if ( INITRD_S_ISBLK(mode) ) result |= S_IFBLK;
+ if ( INITRD_S_ISDIR(mode) ) result |= S_IFDIR;
+ if ( INITRD_S_ISCHR(mode) ) result |= S_IFCHR;
+ if ( INITRD_S_ISFIFO(mode) ) result |= S_IFIFO;
return result;
}
-__attribute__((unused))
-static mode_t InitRDModeToHost(uint32_t mode)
+static initrd_inode_t* initrd_get_inode(struct initrd_context* ctx, uint32_t inode)
{
- mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition.
- if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; }
- if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; }
- if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; }
- if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; }
- if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; }
- if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; }
- if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; }
- if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; }
- return result;
+ if ( ctx->sb->inodecount <= inode )
+ return errno = EINVAL, (initrd_inode_t*) NULL;
+ uint32_t pos = ctx->sb->inodeoffset + ctx->sb->inodesize * inode;
+ return (initrd_inode_t*) (ctx->initrd + pos);
}
-uint32_t Root()
+static uint8_t* initrd_inode_get_data(struct initrd_context* ctx, initrd_inode_t* inode, size_t* size)
{
- return sb->root;
+ return *size = inode->size, ctx->initrd + inode->dataoffset;
}
-static const initrd_inode_t* GetInode(uint32_t inode)
+static uint32_t initrd_directory_open(struct initrd_context* ctx, initrd_inode_t* inode, const char* name)
{
- if ( sb->inodecount <= inode ) { errno = EINVAL; return NULL; }
- uint32_t pos = sb->inodeoffset + sb->inodesize * inode;
- return (const initrd_inode_t*) (initrd + pos);
-}
-
-bool Stat(uint32_t ino, struct stat* st)
-{
- const initrd_inode_t* inode = GetInode(ino);
- if ( !inode ) { return false; }
- st->st_ino = ino;
- st->st_mode = HostModeToInitRD(inode->mode);
- st->st_nlink = inode->nlink;
- st->st_uid = inode->uid;
- st->st_gid = inode->gid;
- st->st_size = inode->size;
- st->st_atim = timespec_make(inode->mtime, 0);
- st->st_ctim = timespec_make(inode->ctime, 0);
- st->st_mtim = timespec_make(inode->mtime, 0);
- st->st_blksize = 1;
- st->st_blocks = inode->size;
- return true;
-}
-
-uint8_t* Open(uint32_t ino, size_t* size)
-{
- const initrd_inode_t* inode = GetInode(ino);
- if ( !inode ) { return NULL; }
- *size = inode->size;
- return initrd + inode->dataoffset;
-}
-
-uint32_t Traverse(uint32_t ino, const char* name)
-{
- const initrd_inode_t* inode = GetInode(ino);
- if ( !inode ) { return 0; }
- if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
+ if ( !INITRD_S_ISDIR(inode->mode) )
+ return errno = ENOTDIR, 0;
uint32_t offset = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
- const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos);
+ initrd_dirent* dirent = (initrd_dirent*) (ctx->initrd + pos);
if ( dirent->namelen && !strcmp(dirent->name, name) )
- {
return dirent->inode;
- }
offset += dirent->reclen;
}
- errno = ENOENT;
- return 0;
+ return errno = ENOENT, 0;
}
-const char* GetFilename(uint32_t dir, size_t index)
+static const char* initrd_directory_get_filename(struct initrd_context* ctx, initrd_inode_t* inode, size_t index)
{
- const initrd_inode_t* inode = GetInode(dir);
- if ( !inode ) { return 0; }
- if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
+ if ( !INITRD_S_ISDIR(inode->mode) )
+ return errno = ENOTDIR, (const char*) NULL;
uint32_t offset = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
- const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos);
- if ( index-- == 0 ) { return dirent->name; }
+ initrd_dirent* dirent = (initrd_dirent*) (ctx->initrd + pos);
+ if ( index-- == 0 )
+ return dirent->name;
offset += dirent->reclen;
}
- errno = EINVAL;
- return NULL;
+ return errno = EINVAL, (const char*) NULL;
}
-size_t GetNumFiles(uint32_t dir)
+static size_t initrd_directory_get_num_files(struct initrd_context* ctx, initrd_inode_t* inode)
{
- const initrd_inode_t* inode = GetInode(dir);
- if ( !inode ) { return 0; }
- if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; }
+ if ( !INITRD_S_ISDIR(inode->mode) )
+ return errno = ENOTDIR, 0;
uint32_t offset = 0;
size_t numentries = 0;
while ( offset < inode->size )
{
uint32_t pos = inode->dataoffset + offset;
- const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos);
+ const initrd_dirent* dirent = (const initrd_dirent*) (ctx->initrd + pos);
numentries++;
offset += dirent->reclen;
}
return numentries;
}
-void CheckSum()
+static bool ExtractNode(struct initrd_context* ctx, initrd_inode_t* inode, Ref node);
+
+static bool ExtractFile(struct initrd_context* ctx, initrd_inode_t* inode, Ref file)
{
- uint32_t amount = sb->fssize - sb->sumsize;
- uint8_t* filesum = initrd + amount;
- if ( sb->sumalgorithm != INITRD_ALGO_CRC32 )
+ size_t filesize;
+ uint8_t* data = initrd_inode_get_data(ctx, inode, &filesize);
+ if ( !data )
+ return false;
+ if ( file->truncate(&ctx->ioctx, filesize) != 0 )
+ return false;
+ size_t sofar = 0;
+ while ( sofar < filesize )
{
- Log::PrintF("Warning: InitRD checksum algorithm not supported\n");
- return;
- }
- uint32_t crc32 = *((uint32_t*) filesum);
- uint32_t filecrc32 = CRC32::Hash(initrd, amount);
- uint32_t doublecheck = CRC32::Hash(initrd, amount);
- if ( filecrc32 != doublecheck )
- {
- // Yes, this has happened. This seems like the goto place for such bugs
- // to trigger, so I added a more accurate warning.
- Panic("Calculating InitRD checksum two times gave different results: "
- "this likely means the kernel have a corruption bug, possibly "
- "caused by building libc and kernel with different settings or "
- "a bug in the scheduler/interrupt handler or who knows. "
- "It is also possible that the laws of logic has changed.");
- }
- if ( crc32 != filecrc32 )
- {
- PanicF("InitRD had checksum %X, expected %X: this means the ramdisk "
- "may have been corrupted by the bootloader.", filecrc32, crc32);
+ ssize_t numbytes = file->write(&ctx->ioctx, data + sofar, filesize - sofar);
+ if ( numbytes <= 0 )
+ return false;
+ sofar += numbytes;
}
+ return true;
}
-void Init(addr_t phys, size_t size)
+static bool ExtractDir(struct initrd_context* ctx, initrd_inode_t* inode, Ref dir)
{
- assert(!initrd);
-
- // Allocate the needed kernel virtual address space.
- if ( !AllocateKernelAddress(&initrd_addr_alloc, size) )
- PanicF("Can't allocate 0x%zx bytes of kernel address space for the "
- "init ramdisk", size );
-
- // Map the physical frames onto our address space.
- addr_t mapat = initrd_addr_alloc.from;
- for ( size_t i = 0; i < size; i += Page::Size() )
- if ( !Memory::Map(phys + i, mapat + i, PROT_KREAD) )
- Panic("Unable to map the init ramdisk into virtual memory");
- Memory::Flush();
-
- initrd = (uint8_t*) mapat;
- initrdsize = size;
-
- if ( size < sizeof(*sb) ) { PanicF("initrd is too small"); }
- sb = (const initrd_superblock_t*) initrd;
-
- if ( !String::StartsWith(sb->magic, "sortix-initrd") )
- {
- Panic("Invalid magic value in initrd. This means the ramdisk may have "
- "been corrupted by the bootloader, or that an incompatible file "
- "has been passed to the kernel.");
- }
-
- if ( strcmp(sb->magic, "sortix-initrd-1") == 0 )
- {
- Panic("Sortix initrd format version 1 is no longer supported.");
- }
-
- if ( strcmp(sb->magic, "sortix-initrd-2") != 0 )
- {
- Panic("The initrd has a format that isn't supported. Perhaps it is "
- "too new? Try downgrade or regenerate the initrd.");
- }
-
- if ( size < sb->fssize )
- {
- PanicF("The initrd said it is %u bytes, but the kernel was only passed "
- "%zu bytes by the bootloader, which is not enough.", sb->fssize,
- size);
- }
-
- CheckSum();
-}
-
-static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref node, Ref links);
-static bool ExtractFile(ioctx_t* ctx, uint32_t ino, Ref file);
-static bool ExtractNode(ioctx_t* ctx, uint32_t ino, Ref node, Ref links);
-
-static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref dir, Ref links)
-{
- size_t numfiles = GetNumFiles(ino);
+ size_t numfiles = initrd_directory_get_num_files(ctx, inode);
for ( size_t i = 0; i < numfiles; i++ )
{
- const char* name = GetFilename(ino, i);
+ const char* name = initrd_directory_get_filename(ctx, inode, i);
if ( !name )
return false;
if ( IsDotOrDotDot(name) )
continue;
- uint32_t childino = Traverse(ino, name);
+ uint32_t childino = initrd_directory_open(ctx, inode, name);
if ( !childino )
return false;
- initrd_inode_t* child = (initrd_inode_t*) GetInode(childino);
- mode_t mode = InitRDModeToHost(child->mode);
+ initrd_inode_t* child = (initrd_inode_t*) initrd_get_inode(ctx, childino);
+ mode_t mode = initrd_mode_to_host_mode(child->mode);
if ( INITRD_S_ISDIR(child->mode) )
{
- if ( dir->mkdir(ctx, name, mode) && errno != EEXIST )
+ if ( dir->mkdir(&ctx->ioctx, name, mode) && errno != EEXIST )
return false;
- Ref desc = dir->open(ctx, name, O_SEARCH | O_DIRECTORY, 0);
+ Ref desc = dir->open(&ctx->ioctx, name, O_SEARCH | O_DIRECTORY, 0);
if ( !desc )
return false;
- if ( !ExtractNode(ctx, childino, desc, links) )
+ if ( !ExtractNode(ctx, child, desc) )
return false;
}
if ( INITRD_S_ISREG(child->mode) )
@@ -293,21 +191,21 @@ static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref dir, Refnlink != 0);
char link_path[sizeof(childino) * 3];
snprintf(link_path, sizeof(link_path), "%ju", (uintmax_t) childino);
- Ref existing(links->open(ctx, link_path, O_READ, 0));
- if ( !existing || dir->link(ctx, name, existing) != 0 )
+ Ref existing(ctx->links->open(&ctx->ioctx, link_path, O_READ, 0));
+ if ( !existing || dir->link(&ctx->ioctx, name, existing) != 0 )
{
- Ref desc(dir->open(ctx, name, O_WRITE | O_CREATE, mode));
+ Ref desc(dir->open(&ctx->ioctx, name, O_WRITE | O_CREATE, mode));
if ( !desc )
return false;
- if ( !ExtractNode(ctx, childino, desc, links) )
+ if ( !ExtractNode(ctx, child, desc) )
return false;
if ( 2 <= child->nlink )
- links->link(ctx, link_path, desc);
+ ctx->links->link(&ctx->ioctx, link_path, desc);
}
if ( --child->nlink == 0 && INITRD_S_ISREG(child->mode) )
{
size_t filesize;
- const uint8_t* data = Open(childino, &filesize);
+ const uint8_t* data = initrd_inode_get_data(ctx, child, &filesize);
uintptr_t from = (uintptr_t) data;
uintptr_t size = filesize;
uintptr_t from_aligned = Page::AlignUp(from);
@@ -325,60 +223,91 @@ static bool ExtractDir(ioctx_t* ctx, uint32_t ino, Ref dir, Ref file)
+static bool ExtractNode(struct initrd_context* ctx, initrd_inode_t* inode, Ref node)
{
- size_t filesize;
- const uint8_t* data = Open(ino, &filesize);
- if ( !data )
+ if ( node->chmod(&ctx->ioctx, initrd_mode_to_host_mode(inode->mode)) < 0 )
return false;
- if ( file->truncate(ctx, filesize) != 0 )
- return false;
- size_t sofar = 0;
- while ( sofar < filesize )
- {
- ssize_t numbytes = file->write(ctx, data + sofar, filesize - sofar);
- if ( numbytes <= 0 )
- return false;
- sofar += numbytes;
- }
- return true;
-}
-
-static bool ExtractNode(ioctx_t* ctx, uint32_t ino, Ref node, Ref links)
-{
- const initrd_inode_t* inode = GetInode(ino);
- if ( !inode )
- return false;
- if ( node->chmod(ctx, InitRDModeToHost(inode->mode)) < 0 )
- return false;
- if ( node->chown(ctx, inode->uid, inode->gid) < 0 )
+ if ( node->chown(&ctx->ioctx, inode->uid, inode->gid) < 0 )
return false;
if ( INITRD_S_ISDIR(inode->mode) )
- if ( !ExtractDir(ctx, ino, node, links) )
+ if ( !ExtractDir(ctx, inode, node) )
return false;
if ( INITRD_S_ISREG(inode->mode) )
- if ( !ExtractFile(ctx, ino, node) )
+ if ( !ExtractFile(ctx, inode, node) )
return false;
struct timespec ctime = timespec_make((time_t) inode->ctime, 0);
struct timespec mtime = timespec_make((time_t) inode->mtime, 0);
- if ( node->utimens(ctx, &mtime, &ctime, &mtime) < 0 )
+ if ( node->utimens(&ctx->ioctx, &mtime, &ctime, &mtime) < 0 )
return false;
return true;
}
bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref desc)
{
- Init(physaddr, size);
+ // Allocate the needed kernel virtual address space.
+ addralloc_t initrd_addr_alloc;
+ if ( !AllocateKernelAddress(&initrd_addr_alloc, size) )
+ PanicF("Can't allocate 0x%zx bytes of kernel address space for the "
+ "init ramdisk", size );
- ioctx_t ctx; SetupKernelIOCtx(&ctx);
- if ( desc->mkdir(&ctx, ".initrd-links", 0777) != 0 )
+ // Map the physical frames onto our address space.
+ addr_t mapat = initrd_addr_alloc.from;
+ for ( size_t i = 0; i < size; i += Page::Size() )
+ if ( !Memory::Map(physaddr + i, mapat + i, PROT_KREAD) )
+ PanicF("Unable to map the init ramdisk into virtual memory");
+ Memory::Flush();
+
+ struct initrd_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.initrd = (uint8_t*) mapat;
+ ctx.initrd_size = size;
+ SetupKernelIOCtx(&ctx.ioctx);
+
+ if ( size < sizeof(*ctx.sb) )
+ PanicF("initrd is too small");
+ ctx.sb = (initrd_superblock_t*) ctx.initrd;
+
+ if ( !String::StartsWith(ctx.sb->magic, "sortix-initrd") )
+ PanicF("Invalid magic value in initrd. This means the ramdisk may have "
+ "been corrupted by the bootloader, or that an incompatible file "
+ "has been passed to the kernel.");
+
+ if ( strcmp(ctx.sb->magic, "sortix-initrd-1") == 0 )
+ PanicF("Sortix initrd format version 1 is no longer supported.");
+
+ if ( strcmp(ctx.sb->magic, "sortix-initrd-2") != 0 )
+ PanicF("The initrd has a format that isn't supported. Perhaps it is "
+ "too new? Try downgrade or regenerate the initrd.");
+
+ if ( size < ctx.sb->fssize )
+ PanicF("The initrd said it is %u bytes, but the kernel was only passed "
+ "%zu bytes by the bootloader, which is not enough.",
+ ctx.sb->fssize, size);
+
+ uint32_t amount = ctx.sb->fssize - ctx.sb->sumsize;
+ uint8_t* filesum = ctx.initrd + amount;
+ if ( ctx.sb->sumalgorithm != INITRD_ALGO_CRC32 )
+ {
+ Log::PrintF("Warning: InitRD checksum algorithm not supported\n");
+ }
+ else
+ {
+ uint32_t crc32 = *((uint32_t*) filesum);
+ uint32_t filecrc32 = CRC32::Hash(ctx.initrd, amount);
+ if ( crc32 != filecrc32 )
+ {
+ PanicF("InitRD had checksum %X, expected %X: this means the ramdisk "
+ "may have been corrupted by the bootloader.", filecrc32, crc32);
+ }
+ }
+
+ if ( desc->mkdir(&ctx.ioctx, ".initrd-links", 0777) != 0 )
return false;
- Ref links(desc->open(&ctx, ".initrd-links", O_READ | O_DIRECTORY, 0));
- if ( !links )
+ if ( !(ctx.links = desc->open(&ctx.ioctx, ".initrd-links", O_READ | O_DIRECTORY, 0)) )
return false;
- if ( !ExtractNode(&ctx, sb->root, desc, links) )
+ if ( !ExtractNode(&ctx, initrd_get_inode(&ctx, ctx.sb->root), desc) )
return false;
union
@@ -387,20 +316,21 @@ bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref desc)
uint8_t dirent_data[sizeof(struct kernel_dirent) + sizeof(uintmax_t) * 3];
};
- while ( 0 < links->readdirents(&ctx, &dirent, sizeof(dirent_data), 1) &&
+ while ( 0 < ctx.links->readdirents(&ctx.ioctx, &dirent, sizeof(dirent_data), 1) &&
((const char*) dirent.d_name)[0] )
{
if ( ((const char*) dirent.d_name)[0] == '.' )
continue;
- links->unlink(&ctx, dirent.d_name);
- links->lseek(&ctx, 0, SEEK_SET);
+ ctx.links->unlink(&ctx.ioctx, dirent.d_name);
+ ctx.links->lseek(&ctx.ioctx, 0, SEEK_SET);
}
- desc->rmdir(&ctx, ".initrd-links");
+ ctx.links.Reset();
+
+ desc->rmdir(&ctx.ioctx, ".initrd-links");
// Unmap the pages and return the physical frames for reallocation.
- addr_t mapat = initrd_addr_alloc.from;
- for ( size_t i = 0; i < initrdsize; i += Page::Size() )
+ for ( size_t i = 0; i < initrd_addr_alloc.size; i += Page::Size() )
{
if ( !Memory::LookUp(mapat + i, NULL, NULL) )
continue;
@@ -409,8 +339,6 @@ bool ExtractFromPhysicalInto(addr_t physaddr, size_t size, Ref desc)
}
Memory::Flush();
- initrdsize = 0;
-
// Free the used virtual address space.
FreeKernelAddress(&initrd_addr_alloc);