From ae73391a10545d54180239043565ceeb228d7da6 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 9 May 2014 01:32:51 +0200 Subject: [PATCH] Clean up initrd extraction code. --- kernel/initrd.cpp | 368 +++++++++++++++++++--------------------------- 1 file changed, 148 insertions(+), 220 deletions(-) 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);